@apollo/federation-internals 2.0.0-alpha.4 → 2.0.0-preview.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/CHANGELOG.md +11 -1
- package/dist/buildSchema.d.ts +7 -3
- package/dist/buildSchema.d.ts.map +1 -1
- package/dist/buildSchema.js +41 -22
- package/dist/buildSchema.js.map +1 -1
- package/dist/coreSpec.d.ts +26 -4
- package/dist/coreSpec.d.ts.map +1 -1
- package/dist/coreSpec.js +86 -25
- package/dist/coreSpec.js.map +1 -1
- package/dist/definitions.d.ts +50 -43
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +201 -217
- package/dist/definitions.js.map +1 -1
- package/dist/directiveAndTypeSpecification.d.ts +38 -0
- package/dist/directiveAndTypeSpecification.d.ts.map +1 -0
- package/dist/directiveAndTypeSpecification.js +196 -0
- package/dist/directiveAndTypeSpecification.js.map +1 -0
- package/dist/error.d.ts +10 -1
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +22 -2
- package/dist/error.js.map +1 -1
- package/dist/extractSubgraphsFromSupergraph.d.ts.map +1 -1
- package/dist/extractSubgraphsFromSupergraph.js +29 -94
- package/dist/extractSubgraphsFromSupergraph.js.map +1 -1
- package/dist/federation.d.ts +88 -46
- package/dist/federation.d.ts.map +1 -1
- package/dist/federation.js +745 -233
- package/dist/federation.js.map +1 -1
- package/dist/federationSpec.d.ts +19 -0
- package/dist/federationSpec.d.ts.map +1 -0
- package/dist/federationSpec.js +91 -0
- package/dist/federationSpec.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/joinSpec.d.ts +1 -0
- package/dist/joinSpec.d.ts.map +1 -1
- package/dist/joinSpec.js +1 -0
- package/dist/joinSpec.js.map +1 -1
- package/dist/operations.d.ts +8 -1
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +11 -4
- package/dist/operations.js.map +1 -1
- package/dist/print.d.ts +11 -9
- package/dist/print.d.ts.map +1 -1
- package/dist/print.js +21 -11
- package/dist/print.js.map +1 -1
- package/dist/schemaUpgrader.d.ts +108 -0
- package/dist/schemaUpgrader.d.ts.map +1 -0
- package/dist/schemaUpgrader.js +498 -0
- package/dist/schemaUpgrader.js.map +1 -0
- package/dist/sharing.d.ts +3 -0
- package/dist/sharing.d.ts.map +1 -0
- package/dist/sharing.js +51 -0
- package/dist/sharing.js.map +1 -0
- package/dist/supergraphs.d.ts.map +1 -1
- package/dist/supergraphs.js +2 -3
- package/dist/supergraphs.js.map +1 -1
- package/dist/tagSpec.d.ts.map +1 -1
- package/dist/tagSpec.js +1 -3
- package/dist/tagSpec.js.map +1 -1
- package/dist/utils.d.ts +8 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +49 -1
- package/dist/utils.js.map +1 -1
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +9 -4
- package/dist/validate.js.map +1 -1
- package/dist/validation/KnownTypeNamesInFederationRule.d.ts.map +1 -1
- package/dist/validation/KnownTypeNamesInFederationRule.js +1 -2
- package/dist/validation/KnownTypeNamesInFederationRule.js.map +1 -1
- package/dist/values.d.ts +1 -0
- package/dist/values.d.ts.map +1 -1
- package/dist/values.js +3 -2
- package/dist/values.js.map +1 -1
- package/jest.config.js +5 -1
- package/package.json +4 -7
- package/src/__tests__/definitions.test.ts +19 -17
- package/src/__tests__/extractSubgraphsFromSupergraph.test.ts +103 -0
- package/src/__tests__/federation.test.ts +31 -0
- package/src/__tests__/operations.test.ts +2 -3
- package/src/__tests__/schemaUpgrader.test.ts +168 -0
- package/src/__tests__/subgraphValidation.test.ts +33 -19
- package/src/__tests__/values.test.ts +2 -4
- package/src/buildSchema.ts +55 -36
- package/src/coreSpec.ts +112 -31
- package/src/definitions.ts +247 -260
- package/src/directiveAndTypeSpecification.ts +276 -0
- package/src/error.ts +61 -5
- package/src/extractSubgraphsFromSupergraph.ts +35 -119
- package/src/federation.ts +960 -293
- package/src/federationSpec.ts +113 -0
- package/src/index.ts +2 -0
- package/src/joinSpec.ts +2 -1
- package/src/operations.ts +22 -7
- package/src/print.ts +51 -38
- package/src/schemaUpgrader.ts +657 -0
- package/src/sharing.ts +68 -0
- package/src/supergraphs.ts +3 -3
- package/src/tagSpec.ts +1 -3
- package/src/utils.ts +85 -0
- package/src/validate.ts +13 -7
- package/src/validation/KnownTypeNamesInFederationRule.ts +1 -7
- package/src/values.ts +7 -3
- package/tsconfig.test.tsbuildinfo +1 -1
- package/tsconfig.tsbuildinfo +1 -1
package/src/definitions.ts
CHANGED
|
@@ -12,16 +12,24 @@ import {
|
|
|
12
12
|
ListTypeNode,
|
|
13
13
|
NamedTypeNode,
|
|
14
14
|
parse,
|
|
15
|
-
printError,
|
|
16
15
|
TypeNode,
|
|
17
16
|
VariableDefinitionNode,
|
|
18
17
|
VariableNode
|
|
19
18
|
} from "graphql";
|
|
20
|
-
import {
|
|
21
|
-
|
|
19
|
+
import {
|
|
20
|
+
CoreImport,
|
|
21
|
+
CoreOrLinkDirectiveArgs,
|
|
22
|
+
CoreSpecDefinition,
|
|
23
|
+
extractCoreFeatureImports,
|
|
24
|
+
FeatureUrl,
|
|
25
|
+
findCoreSpecVersion,
|
|
26
|
+
isCoreSpecDirectiveApplication,
|
|
27
|
+
removeFeatureElements,
|
|
28
|
+
} from "./coreSpec";
|
|
29
|
+
import { assert, mapValues, MapWithCachedArrays, setValues } from "./utils";
|
|
22
30
|
import { withDefaultValues, valueEquals, valueToString, valueToAST, variablesInValue, valueFromAST, valueNodeToConstValueNode } from "./values";
|
|
23
31
|
import { removeInaccessibleElements } from "./inaccessibleSpec";
|
|
24
|
-
import {
|
|
32
|
+
import { defaultPrintOptions, printSchema } from './print';
|
|
25
33
|
import { sameType } from './types';
|
|
26
34
|
import { addIntrospectionFields, introspectionFieldNames, isIntrospectionName } from "./introspection";
|
|
27
35
|
import { err } from '@apollo/core-schema';
|
|
@@ -30,6 +38,7 @@ import { validateSDL } from "graphql/validation/validate";
|
|
|
30
38
|
import { SDLValidationRule } from "graphql/validation/ValidationContext";
|
|
31
39
|
import { specifiedSDLRules } from "graphql/validation/specifiedRules";
|
|
32
40
|
import { validateSchema } from "./validate";
|
|
41
|
+
import { createDirectiveSpecification, createScalarTypeSpecification, DirectiveSpecification, TypeSpecification } from "./directiveAndTypeSpecification";
|
|
33
42
|
|
|
34
43
|
const validationErrorCode = 'GraphQLValidationFailed';
|
|
35
44
|
|
|
@@ -61,11 +70,11 @@ export function printGraphQLErrorsOrRethrow(e: Error): string {
|
|
|
61
70
|
if (!causes) {
|
|
62
71
|
throw e;
|
|
63
72
|
}
|
|
64
|
-
return causes.map(e =>
|
|
73
|
+
return causes.map(e => e.toString()).join('\n\n');
|
|
65
74
|
}
|
|
66
75
|
|
|
67
76
|
export function printErrors(errors: GraphQLError[]): string {
|
|
68
|
-
return errors.map(e =>
|
|
77
|
+
return errors.map(e => e.toString()).join('\n\n');
|
|
69
78
|
}
|
|
70
79
|
|
|
71
80
|
export const typenameFieldName = '__typename';
|
|
@@ -93,6 +102,10 @@ function checkDefaultSchemaRoot(type: NamedType): SchemaRootKind | undefined {
|
|
|
93
102
|
}
|
|
94
103
|
}
|
|
95
104
|
|
|
105
|
+
export function isSchemaRootType(type: NamedType): boolean {
|
|
106
|
+
return isObjectType(type) && type.isRootType();
|
|
107
|
+
}
|
|
108
|
+
|
|
96
109
|
export type Type = NamedType | WrapperType;
|
|
97
110
|
export type NamedType = ScalarType | ObjectType | InterfaceType | UnionType | EnumType | InputObjectType;
|
|
98
111
|
export type OutputType = ScalarType | ObjectType | InterfaceType | UnionType | EnumType | ListType<any> | NonNullType<any>;
|
|
@@ -131,7 +144,7 @@ export function isScalarType(type: Type): type is ScalarType {
|
|
|
131
144
|
}
|
|
132
145
|
|
|
133
146
|
export function isCustomScalarType(type: Type): boolean {
|
|
134
|
-
return isScalarType(type) && !
|
|
147
|
+
return isScalarType(type) && !graphQLBuiltInTypes.includes(type.name);
|
|
135
148
|
}
|
|
136
149
|
|
|
137
150
|
export function isIntType(type: Type): boolean {
|
|
@@ -490,7 +503,7 @@ export abstract class SchemaElement<TOwnType extends SchemaElement<any, TParent>
|
|
|
490
503
|
let name: string;
|
|
491
504
|
if (typeof nameOrDefOrDirective === 'string') {
|
|
492
505
|
this.checkUpdate();
|
|
493
|
-
const def = this.schema().directive(nameOrDefOrDirective);
|
|
506
|
+
const def = this.schema().directive(nameOrDefOrDirective) ?? this.schema().blueprint.onMissingDirectiveDefinition(this.schema(), nameOrDefOrDirective);
|
|
494
507
|
if (!def) {
|
|
495
508
|
throw new GraphQLError(`Cannot apply unknown directive "@${nameOrDefOrDirective}"`);
|
|
496
509
|
}
|
|
@@ -636,6 +649,18 @@ abstract class BaseNamedType<TReferencer, TOwnType extends NamedType & NamedSche
|
|
|
636
649
|
return extension;
|
|
637
650
|
}
|
|
638
651
|
|
|
652
|
+
removeExtensions() {
|
|
653
|
+
if (this._extensions.size === 0) {
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
this._extensions.clear();
|
|
658
|
+
for (const directive of this._appliedDirectives) {
|
|
659
|
+
directive.removeOfExtension();
|
|
660
|
+
}
|
|
661
|
+
this.removeInnerElementsExtensions();
|
|
662
|
+
}
|
|
663
|
+
|
|
639
664
|
isIntrospectionType(): boolean {
|
|
640
665
|
return isIntrospectionName(this.name);
|
|
641
666
|
}
|
|
@@ -649,6 +674,7 @@ abstract class BaseNamedType<TReferencer, TOwnType extends NamedType & NamedSche
|
|
|
649
674
|
}
|
|
650
675
|
|
|
651
676
|
protected abstract hasNonExtensionInnerElements(): boolean;
|
|
677
|
+
protected abstract removeInnerElementsExtensions(): void;
|
|
652
678
|
|
|
653
679
|
protected isElementBuiltIn(): boolean {
|
|
654
680
|
return this.isBuiltIn;
|
|
@@ -773,6 +799,10 @@ abstract class BaseExtensionMember<TExtended extends ExtendableElement> extends
|
|
|
773
799
|
return this._extension;
|
|
774
800
|
}
|
|
775
801
|
|
|
802
|
+
removeOfExtension() {
|
|
803
|
+
this._extension = undefined;
|
|
804
|
+
}
|
|
805
|
+
|
|
776
806
|
setOfExtension(extension: Extension<TExtended> | undefined) {
|
|
777
807
|
this.checkUpdate();
|
|
778
808
|
// See similar comment on FieldDefinition.setOfExtension for why we have to cast.
|
|
@@ -792,224 +822,51 @@ abstract class BaseExtensionMember<TExtended extends ExtendableElement> extends
|
|
|
792
822
|
protected abstract removeInner(): void;
|
|
793
823
|
}
|
|
794
824
|
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
export class BuiltIns {
|
|
800
|
-
readonly defaultGraphQLBuiltInTypes: readonly string[] = [ 'Int', 'Float', 'String', 'Boolean', 'ID' ];
|
|
801
|
-
private readonly defaultGraphQLBuiltInDirectives: readonly string[] = [ 'include', 'skip', 'deprecated', 'specifiedBy' ];
|
|
802
|
-
|
|
803
|
-
addBuiltInTypes(schema: Schema) {
|
|
804
|
-
this.defaultGraphQLBuiltInTypes.forEach(t => this.addBuiltInScalar(schema, t));
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
addBuiltInDirectives(schema: Schema) {
|
|
808
|
-
for (const name of ['include', 'skip']) {
|
|
809
|
-
this.addBuiltInDirective(schema, name)
|
|
810
|
-
.addLocations(DirectiveLocation.FIELD, DirectiveLocation.FRAGMENT_SPREAD, DirectiveLocation.INLINE_FRAGMENT)
|
|
811
|
-
.addArgument('if', new NonNullType(schema.booleanType()));
|
|
812
|
-
}
|
|
813
|
-
this.addBuiltInDirective(schema, 'deprecated')
|
|
814
|
-
.addLocations(
|
|
815
|
-
DirectiveLocation.FIELD_DEFINITION,
|
|
816
|
-
DirectiveLocation.ENUM_VALUE,
|
|
817
|
-
DirectiveLocation.ARGUMENT_DEFINITION,
|
|
818
|
-
DirectiveLocation.INPUT_FIELD_DEFINITION,
|
|
819
|
-
).addArgument('reason', schema.stringType(), 'No longer supported');
|
|
820
|
-
this.addBuiltInDirective(schema, 'specifiedBy')
|
|
821
|
-
.addLocations(DirectiveLocation.SCALAR)
|
|
822
|
-
.addArgument('url', new NonNullType(schema.stringType()));
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
isGraphQLBuiltIn(element: NamedType | DirectiveDefinition | FieldDefinition<any>): boolean {
|
|
826
|
-
if (isIntrospectionName(element.name)) {
|
|
827
|
-
return true;
|
|
828
|
-
}
|
|
829
|
-
if (element instanceof FieldDefinition) {
|
|
830
|
-
return false;
|
|
831
|
-
} else if (element instanceof DirectiveDefinition) {
|
|
832
|
-
return this.defaultGraphQLBuiltInDirectives.includes(element.name);
|
|
833
|
-
} else {
|
|
834
|
-
return this.defaultGraphQLBuiltInTypes.includes(element.name);
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
prepareValidation(_: Schema) {
|
|
839
|
-
// No-op for graphQL built-ins, but overriden for federation built-ins.
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
onValidation(schema: Schema, unvalidatedDirectives?: string[]): GraphQLError[] {
|
|
843
|
-
const errors: GraphQLError[] = [];
|
|
844
|
-
// We make sure that if any of the built-ins has been redefined, then the redefinition is
|
|
845
|
-
// the same as the built-in one.
|
|
846
|
-
for (const type of schema.builtInTypes(undefined, true)) {
|
|
847
|
-
const maybeRedefined = schema.type(type.name)!;
|
|
848
|
-
if (!maybeRedefined.isBuiltIn) {
|
|
849
|
-
this.ensureSameTypeStructure(type, maybeRedefined, errors);
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
for (const directive of schema.builtInDirectives(true)) {
|
|
854
|
-
if (unvalidatedDirectives && unvalidatedDirectives.includes(directive.name)) {
|
|
855
|
-
continue;
|
|
856
|
-
}
|
|
857
|
-
const maybeRedefined = schema.directive(directive.name)!;
|
|
858
|
-
if (!maybeRedefined.isBuiltIn) {
|
|
859
|
-
this.ensureSameDirectiveStructure(directive, maybeRedefined, errors);
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
return errors;
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
validationRules(): readonly SDLValidationRule[] {
|
|
866
|
-
return specifiedSDLRules;
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
maybeUpdateSubgraphDocument(_: Schema, document: DocumentNode): DocumentNode {
|
|
870
|
-
return document;
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
private ensureSameDirectiveStructure(builtIn: DirectiveDefinition<any>, manuallyDefined: DirectiveDefinition<any>, errors: GraphQLError[]) {
|
|
874
|
-
this.ensureSameArguments(builtIn, manuallyDefined, `directive ${builtIn}`, errors);
|
|
875
|
-
// It's ok to say you'll never repeat a built-in that is repeatable. It's not ok to repeat one that isn't.
|
|
876
|
-
if (!builtIn.repeatable && manuallyDefined.repeatable) {
|
|
877
|
-
errors.push(error(`Invalid redefinition of built-in directive ${builtIn}: ${builtIn} should${builtIn.repeatable ? "" : " not"} be repeatable`));
|
|
878
|
-
}
|
|
879
|
-
// Similarly, it's ok to say that you will never use a directive in some locations, but not that you will use it in places not allowed by the built-in.
|
|
880
|
-
if (!manuallyDefined.locations.every(loc => builtIn.locations.includes(loc))) {
|
|
881
|
-
errors.push(error(`Invalid redefinition of built-in directive ${builtIn}: ${builtIn} should have locations ${builtIn.locations.join(', ')}, but found (non-subset) ${manuallyDefined.locations.join(', ')}`));
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
private ensureSameArguments(
|
|
886
|
-
builtIn: { arguments(): readonly ArgumentDefinition<any>[] },
|
|
887
|
-
manuallyDefined: { argument(name: string): ArgumentDefinition<any> | undefined, arguments(): readonly ArgumentDefinition<any>[] },
|
|
888
|
-
what: string,
|
|
889
|
-
errors: GraphQLError[]
|
|
890
|
-
) {
|
|
891
|
-
const expectedArguments = builtIn.arguments();
|
|
892
|
-
const foundArguments = manuallyDefined.arguments();
|
|
893
|
-
if (expectedArguments.length !== foundArguments.length) {
|
|
894
|
-
errors.push(error(`Invalid redefinition of built-in ${what}: should have ${expectedArguments.length} arguments but ${foundArguments.length} found in redefinition`));
|
|
895
|
-
return;
|
|
896
|
-
}
|
|
897
|
-
for (const expectedArgument of expectedArguments) {
|
|
898
|
-
const foundArgument = manuallyDefined.argument(expectedArgument.name)!;
|
|
899
|
-
const expectedType = expectedArgument.type!;
|
|
900
|
-
let actualType = foundArgument.type!;
|
|
901
|
-
if (isNonNullType(actualType) && !isNonNullType(expectedType)) {
|
|
902
|
-
// It's ok to redefine an optional argument as mandatory. For instance, if you want to force people on your team to provide a "deprecation reason", you can
|
|
903
|
-
// redefine @deprecated as `directive @deprecated(reason: String!)...` to get validation. In other words, you are allowed to always pass an argument that
|
|
904
|
-
// is optional if you so wish.
|
|
905
|
-
actualType = actualType.ofType;
|
|
906
|
-
}
|
|
907
|
-
if (!sameType(expectedType, actualType)) {
|
|
908
|
-
errors.push(error(`Invalid redefinition of built-in ${what}: ${expectedArgument.coordinate} should have type ${expectedArgument.type!} but found type ${foundArgument.type!}`));
|
|
909
|
-
} else if (!isNonNullType(actualType) && !valueEquals(expectedArgument.defaultValue, foundArgument.defaultValue)) {
|
|
910
|
-
errors.push(error(`Invalid redefinition of built-in ${what}: ${expectedArgument.coordinate} should have default value ${valueToString(expectedArgument.defaultValue)} but found default value ${valueToString(foundArgument.defaultValue)}`));
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
private ensureSameTypeStructure(builtIn: NamedType, manuallyDefined: NamedType, errors: GraphQLError[]) {
|
|
916
|
-
if (builtIn.kind !== manuallyDefined.kind) {
|
|
917
|
-
errors.push(error(`Invalid redefinition of built-in type ${builtIn}: ${builtIn} should be a ${builtIn.kind} type but redefined as a ${manuallyDefined.kind}`));
|
|
918
|
-
return;
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
switch (builtIn.kind) {
|
|
922
|
-
case 'ScalarType':
|
|
923
|
-
// Nothing more to check for scalars.
|
|
924
|
-
return;
|
|
925
|
-
case 'ObjectType':
|
|
926
|
-
const redefinedObject = manuallyDefined as ObjectType;
|
|
927
|
-
for (const builtInField of builtIn.fields()) {
|
|
928
|
-
const redefinedField = redefinedObject.field(builtInField.name);
|
|
929
|
-
if (!redefinedField) {
|
|
930
|
-
errors.push(error(`Invalid redefinition of built-in type ${builtIn}: redefinition is missing field ${builtInField}`));
|
|
931
|
-
return;
|
|
932
|
-
}
|
|
933
|
-
// We allow adding non-nullability because we've seen redefinition of the federation _Service type with type String! for the `sdl` field
|
|
934
|
-
// and we don't want to break backward compatibility as this doesn't feel too harmful.
|
|
935
|
-
let rType = redefinedField.type!;
|
|
936
|
-
if (!isNonNullType(builtInField.type!) && isNonNullType(rType)) {
|
|
937
|
-
rType = rType.ofType;
|
|
938
|
-
}
|
|
939
|
-
if (!sameType(builtInField.type!, rType)) {
|
|
940
|
-
errors.push(error(`Invalid redefinition of field ${builtInField} of built-in type ${builtIn}: should have type ${builtInField.type} but redefined with type ${redefinedField.type}`));
|
|
941
|
-
return;
|
|
942
|
-
}
|
|
943
|
-
this.ensureSameArguments(builtInField, redefinedField, `field ${builtInField.coordinate}`, errors);
|
|
944
|
-
}
|
|
945
|
-
break;
|
|
946
|
-
case 'UnionType':
|
|
947
|
-
const redefinedUnion = manuallyDefined as UnionType;
|
|
948
|
-
const builtInMembers = sortedMemberNames(builtIn);
|
|
949
|
-
const redefinedMembers = sortedMemberNames(redefinedUnion);
|
|
950
|
-
if (!arrayEquals(builtInMembers, redefinedMembers)) {
|
|
951
|
-
errors.push(error(`Invalid redefinition of built-in type ${builtIn}: redefinition has members [${redefinedMembers}] but should have members [${builtInMembers}]`));
|
|
952
|
-
}
|
|
953
|
-
break;
|
|
954
|
-
default:
|
|
955
|
-
// Let's not bother with the rest until we actually need it.
|
|
956
|
-
errors.push(error(`Invalid redefinition of built-in type ${builtIn}: cannot redefine ${builtIn.kind} built-in types`));
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
protected addBuiltInScalar(schema: Schema, name: string): ScalarType {
|
|
961
|
-
return schema.addType(new ScalarType(name, true));
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
protected addBuiltInObject(schema: Schema, name: string): ObjectType {
|
|
965
|
-
return schema.addType(new ObjectType(name, true));
|
|
825
|
+
export class SchemaBlueprint {
|
|
826
|
+
onMissingDirectiveDefinition(_schema: Schema, _name: string): DirectiveDefinition | undefined {
|
|
827
|
+
// No-op by default, but used for federation.
|
|
828
|
+
return undefined;
|
|
966
829
|
}
|
|
967
830
|
|
|
968
|
-
|
|
969
|
-
|
|
831
|
+
onDirectiveDefinitionAndSchemaParsed(_: Schema) {
|
|
832
|
+
// No-op by default, but used for federation.
|
|
970
833
|
}
|
|
971
834
|
|
|
972
|
-
|
|
973
|
-
|
|
835
|
+
ignoreParsedField(_type: NamedType, _fieldName: string): boolean {
|
|
836
|
+
// No-op by default, but used for federation.
|
|
837
|
+
return false;
|
|
974
838
|
}
|
|
975
839
|
|
|
976
|
-
|
|
977
|
-
|
|
840
|
+
onConstructed(_: Schema) {
|
|
841
|
+
// No-op by default, but used for federation.
|
|
978
842
|
}
|
|
979
843
|
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
name: string
|
|
983
|
-
): DirectiveDefinition<TApplicationArgs> {
|
|
984
|
-
const directive = schema.directive(name);
|
|
985
|
-
if (!directive) {
|
|
986
|
-
throw new Error(`The provided schema has not be built with the ${name} directive built-in`);
|
|
987
|
-
}
|
|
988
|
-
return directive as DirectiveDefinition<TApplicationArgs>;
|
|
844
|
+
onAddedCoreFeature(_schema: Schema, _feature: CoreFeature) {
|
|
845
|
+
// No-op by default, but used for federation.
|
|
989
846
|
}
|
|
990
847
|
|
|
991
|
-
|
|
992
|
-
|
|
848
|
+
onInvalidation(_: Schema) {
|
|
849
|
+
// No-op by default, but used for federation.
|
|
993
850
|
}
|
|
994
851
|
|
|
995
|
-
|
|
996
|
-
|
|
852
|
+
onValidation(_schema: Schema): GraphQLError[] {
|
|
853
|
+
// No-op by default, but used for federation.
|
|
854
|
+
return []
|
|
997
855
|
}
|
|
998
856
|
|
|
999
|
-
|
|
1000
|
-
return
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
specifiedByDirective(schema: Schema): DirectiveDefinition<{url: string}> {
|
|
1004
|
-
return this.getTypedDirective(schema, 'specifiedBy');
|
|
857
|
+
validationRules(): readonly SDLValidationRule[] {
|
|
858
|
+
return specifiedSDLRules;
|
|
1005
859
|
}
|
|
1006
860
|
}
|
|
1007
861
|
|
|
862
|
+
export const defaultSchemaBlueprint = new SchemaBlueprint();
|
|
863
|
+
|
|
1008
864
|
export class CoreFeature {
|
|
1009
865
|
constructor(
|
|
1010
866
|
readonly url: FeatureUrl,
|
|
1011
867
|
readonly nameInSchema: string,
|
|
1012
868
|
readonly directive: Directive<SchemaDefinition>,
|
|
869
|
+
readonly imports: CoreImport[],
|
|
1013
870
|
readonly purpose?: string,
|
|
1014
871
|
) {
|
|
1015
872
|
}
|
|
@@ -1018,6 +875,19 @@ export class CoreFeature {
|
|
|
1018
875
|
return element.name.startsWith(this.nameInSchema + '__')
|
|
1019
876
|
|| (element.kind === 'DirectiveDefinition' && element.name === this.nameInSchema);
|
|
1020
877
|
}
|
|
878
|
+
|
|
879
|
+
directiveNameInSchema(name: string): string {
|
|
880
|
+
if (name === this.url.name) {
|
|
881
|
+
return this.nameInSchema;
|
|
882
|
+
}
|
|
883
|
+
const elementImport = this.imports.find((i) => i.name.charAt(0) === '@' && i.name.slice(1) === name);
|
|
884
|
+
return elementImport ? (elementImport.as?.slice(1) ?? name) : this.nameInSchema + '__' + name;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
typeNameInSchema(name: string): string {
|
|
888
|
+
const elementImport = this.imports.find((i) => i.name === name);
|
|
889
|
+
return elementImport ? (elementImport.as ?? name) : this.nameInSchema + '__' + name;
|
|
890
|
+
}
|
|
1021
891
|
}
|
|
1022
892
|
|
|
1023
893
|
export class CoreFeatures {
|
|
@@ -1027,9 +897,9 @@ export class CoreFeatures {
|
|
|
1027
897
|
|
|
1028
898
|
constructor(readonly coreItself: CoreFeature) {
|
|
1029
899
|
this.add(coreItself);
|
|
1030
|
-
const coreDef =
|
|
900
|
+
const coreDef = findCoreSpecVersion(coreItself.url);
|
|
1031
901
|
if (!coreDef) {
|
|
1032
|
-
throw error(`Schema uses unknown version ${coreItself.url.version} of the
|
|
902
|
+
throw error(`Schema uses unknown version ${coreItself.url.version} of the ${coreItself.url.name} spec`);
|
|
1033
903
|
}
|
|
1034
904
|
this.coreDefinition = coreDef;
|
|
1035
905
|
}
|
|
@@ -1054,14 +924,17 @@ export class CoreFeatures {
|
|
|
1054
924
|
if (directive.definition?.name !== this.coreItself.nameInSchema) {
|
|
1055
925
|
return undefined;
|
|
1056
926
|
}
|
|
1057
|
-
const
|
|
1058
|
-
const
|
|
927
|
+
const typedDirective = directive as Directive<SchemaDefinition, CoreOrLinkDirectiveArgs>
|
|
928
|
+
const args = typedDirective.arguments();
|
|
929
|
+
const url = this.coreDefinition.extractFeatureUrl(args);
|
|
1059
930
|
const existing = this.byIdentity.get(url.identity);
|
|
1060
931
|
if (existing) {
|
|
1061
932
|
throw error(`Duplicate inclusion of feature ${url.identity}`);
|
|
1062
933
|
}
|
|
1063
|
-
const
|
|
934
|
+
const imports = extractCoreFeatureImports(typedDirective);
|
|
935
|
+
const feature = new CoreFeature(url, args.as ?? url.name, directive, imports, args.for);
|
|
1064
936
|
this.add(feature);
|
|
937
|
+
directive.schema().blueprint.onAddedCoreFeature(directive.schema(), feature);
|
|
1065
938
|
return feature;
|
|
1066
939
|
}
|
|
1067
940
|
|
|
@@ -1069,9 +942,58 @@ export class CoreFeatures {
|
|
|
1069
942
|
this.byAlias.set(feature.nameInSchema, feature);
|
|
1070
943
|
this.byIdentity.set(feature.url.identity, feature);
|
|
1071
944
|
}
|
|
945
|
+
|
|
946
|
+
sourceFeature(element: DirectiveDefinition | NamedType): CoreFeature | undefined {
|
|
947
|
+
const isDirective = element instanceof DirectiveDefinition;
|
|
948
|
+
const splitted = element.name.split('__');
|
|
949
|
+
if (splitted.length > 1) {
|
|
950
|
+
return this.byAlias.get(splitted[0]);
|
|
951
|
+
} else {
|
|
952
|
+
const directFeature = this.byAlias.get(element.name);
|
|
953
|
+
if (directFeature && isDirective) {
|
|
954
|
+
return directFeature;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// Let's see if it's an import. If not, it's not associated to a declared feature.
|
|
958
|
+
const importName = isDirective ? '@' + element.name : element.name;
|
|
959
|
+
const allFeatures = [this.coreItself, ...this.byIdentity.values()];
|
|
960
|
+
for (const feature of allFeatures) {
|
|
961
|
+
for (const { as } of feature.imports) {
|
|
962
|
+
if (as === importName) {
|
|
963
|
+
return feature;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
return undefined;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
1072
970
|
}
|
|
1073
971
|
|
|
1074
|
-
const
|
|
972
|
+
const graphQLBuiltInTypes: readonly string[] = [ 'Int', 'Float', 'String', 'Boolean', 'ID' ];
|
|
973
|
+
const graphQLBuiltInTypesSpecifications: readonly TypeSpecification[] = graphQLBuiltInTypes.map((name) => createScalarTypeSpecification({ name }));
|
|
974
|
+
|
|
975
|
+
const graphQLBuiltInDirectivesSpecifications: readonly DirectiveSpecification[] = [
|
|
976
|
+
createDirectiveSpecification({
|
|
977
|
+
name: 'include',
|
|
978
|
+
locations: [DirectiveLocation.FIELD, DirectiveLocation.FRAGMENT_SPREAD, DirectiveLocation.INLINE_FRAGMENT],
|
|
979
|
+
argumentFct: (schema) => [{ name: 'if', type: new NonNullType(schema.booleanType()) }]
|
|
980
|
+
}),
|
|
981
|
+
createDirectiveSpecification({
|
|
982
|
+
name: 'skip',
|
|
983
|
+
locations: [DirectiveLocation.FIELD, DirectiveLocation.FRAGMENT_SPREAD, DirectiveLocation.INLINE_FRAGMENT],
|
|
984
|
+
argumentFct: (schema) => [{ name: 'if', type: new NonNullType(schema.booleanType()) }]
|
|
985
|
+
}),
|
|
986
|
+
createDirectiveSpecification({
|
|
987
|
+
name: 'deprecated',
|
|
988
|
+
locations: [DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.ENUM_VALUE, DirectiveLocation.ARGUMENT_DEFINITION, DirectiveLocation.INPUT_FIELD_DEFINITION],
|
|
989
|
+
argumentFct: (schema) => [{ name: 'reason', type: schema.stringType(), defaultValue: 'No longer supported' }]
|
|
990
|
+
}),
|
|
991
|
+
createDirectiveSpecification({
|
|
992
|
+
name: 'specifiedBy',
|
|
993
|
+
locations: [DirectiveLocation.SCALAR],
|
|
994
|
+
argumentFct: (schema) => [{ name: 'url', type: new NonNullType(schema.stringType()) }]
|
|
995
|
+
}),
|
|
996
|
+
];
|
|
1075
997
|
|
|
1076
998
|
export class Schema {
|
|
1077
999
|
private _schemaDefinition: SchemaDefinition;
|
|
@@ -1081,16 +1003,17 @@ export class Schema {
|
|
|
1081
1003
|
private readonly _directives = new MapWithCachedArrays<string, DirectiveDefinition>();
|
|
1082
1004
|
private _coreFeatures?: CoreFeatures;
|
|
1083
1005
|
private isConstructed: boolean = false;
|
|
1084
|
-
|
|
1006
|
+
public isValidated: boolean = false;
|
|
1085
1007
|
|
|
1086
1008
|
private cachedDocument?: DocumentNode;
|
|
1087
1009
|
private apiSchema?: Schema;
|
|
1088
1010
|
|
|
1089
|
-
constructor(readonly
|
|
1011
|
+
constructor(readonly blueprint: SchemaBlueprint = defaultSchemaBlueprint) {
|
|
1090
1012
|
this._schemaDefinition = new SchemaDefinition();
|
|
1091
1013
|
Element.prototype['setParent'].call(this._schemaDefinition, this);
|
|
1092
|
-
|
|
1093
|
-
|
|
1014
|
+
graphQLBuiltInTypesSpecifications.forEach((spec) => spec.checkOrAdd(this, undefined, true));
|
|
1015
|
+
graphQLBuiltInDirectivesSpecifications.forEach((spec) => spec.checkOrAdd(this, undefined, true));
|
|
1016
|
+
blueprint.onConstructed(this);
|
|
1094
1017
|
this.isConstructed = true;
|
|
1095
1018
|
}
|
|
1096
1019
|
|
|
@@ -1135,10 +1058,8 @@ export class Schema {
|
|
|
1135
1058
|
}
|
|
1136
1059
|
}
|
|
1137
1060
|
|
|
1138
|
-
private forceSetCachedDocument(document: DocumentNode
|
|
1139
|
-
this.cachedDocument =
|
|
1140
|
-
? this.builtIns.maybeUpdateSubgraphDocument(this, document)
|
|
1141
|
-
: document;
|
|
1061
|
+
private forceSetCachedDocument(document: DocumentNode) {
|
|
1062
|
+
this.cachedDocument = document;
|
|
1142
1063
|
}
|
|
1143
1064
|
|
|
1144
1065
|
isCoreSchema(): boolean {
|
|
@@ -1152,7 +1073,7 @@ export class Schema {
|
|
|
1152
1073
|
toAST(): DocumentNode {
|
|
1153
1074
|
if (!this.cachedDocument) {
|
|
1154
1075
|
// As we're not building the document from a file, having locations info might be more confusing that not.
|
|
1155
|
-
this.forceSetCachedDocument(parse(printSchema(this
|
|
1076
|
+
this.forceSetCachedDocument(parse(printSchema(this), { noLocation: true }));
|
|
1156
1077
|
}
|
|
1157
1078
|
return this.cachedDocument!;
|
|
1158
1079
|
}
|
|
@@ -1185,8 +1106,8 @@ export class Schema {
|
|
|
1185
1106
|
|
|
1186
1107
|
// Some subgraphs, especially federation 1 ones, cannot be properly converted to a GraphQLSchema because they are invalid graphQL.
|
|
1187
1108
|
// And the main culprit is type extensions that don't have a corresponding definition. So to avoid that problem, we print
|
|
1188
|
-
// up the AST without extensions.
|
|
1189
|
-
const ast = parse(printSchema(this, { ...
|
|
1109
|
+
// up the AST without extensions.
|
|
1110
|
+
const ast = parse(printSchema(this, { ...defaultPrintOptions, mergeTypesAndExtensions: true }), { noLocation: true });
|
|
1190
1111
|
return buildGraphqlSchemaFromAST(ast);
|
|
1191
1112
|
}
|
|
1192
1113
|
|
|
@@ -1197,12 +1118,9 @@ export class Schema {
|
|
|
1197
1118
|
/**
|
|
1198
1119
|
* All the types defined on this schema, excluding the built-in types.
|
|
1199
1120
|
*/
|
|
1200
|
-
types<T extends NamedType>(kind?: T['kind']
|
|
1121
|
+
types<T extends NamedType>(kind?: T['kind']): readonly T[] {
|
|
1201
1122
|
const allKinds = this._types.values();
|
|
1202
|
-
|
|
1203
|
-
return includeNonGraphQLBuiltIns
|
|
1204
|
-
? this.builtInTypes(kind).filter(t => !graphQLBuiltIns.isGraphQLBuiltIn(t)).concat(forKind)
|
|
1205
|
-
: forKind;
|
|
1123
|
+
return (kind ? allKinds.filter(t => t.kind === kind) : allKinds) as readonly T[];
|
|
1206
1124
|
}
|
|
1207
1125
|
|
|
1208
1126
|
/**
|
|
@@ -1262,9 +1180,12 @@ export class Schema {
|
|
|
1262
1180
|
|
|
1263
1181
|
addType<T extends NamedType>(type: T): T {
|
|
1264
1182
|
const existing = this.type(type.name);
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1183
|
+
if (existing) {
|
|
1184
|
+
// Like for directive, we let user shadow built-in types, but the definition must be valid.
|
|
1185
|
+
if (existing.isBuiltIn) {
|
|
1186
|
+
} else {
|
|
1187
|
+
throw error(`Type ${type} already exists in this schema`);
|
|
1188
|
+
}
|
|
1268
1189
|
}
|
|
1269
1190
|
if (type.isAttached()) {
|
|
1270
1191
|
// For convenience, let's not error out on adding an already added type.
|
|
@@ -1297,10 +1218,8 @@ export class Schema {
|
|
|
1297
1218
|
/**
|
|
1298
1219
|
* All the directive defined on this schema, excluding the built-in directives.
|
|
1299
1220
|
*/
|
|
1300
|
-
directives(
|
|
1301
|
-
return
|
|
1302
|
-
? this.builtInDirectives().filter(d => !graphQLBuiltIns.isGraphQLBuiltIn(d)).concat(this._directives.values())
|
|
1303
|
-
: this._directives.values();
|
|
1221
|
+
directives(): readonly DirectiveDefinition[] {
|
|
1222
|
+
return this._directives.values();
|
|
1304
1223
|
}
|
|
1305
1224
|
|
|
1306
1225
|
/**
|
|
@@ -1322,7 +1241,11 @@ export class Schema {
|
|
|
1322
1241
|
|
|
1323
1242
|
directive(name: string): DirectiveDefinition | undefined {
|
|
1324
1243
|
const directive = this._directives.get(name);
|
|
1325
|
-
return directive ? directive : this.
|
|
1244
|
+
return directive ? directive : this.builtInDirective(name);
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
builtInDirective(name: string): DirectiveDefinition | undefined {
|
|
1248
|
+
return this._builtInDirectives.get(name);
|
|
1326
1249
|
}
|
|
1327
1250
|
|
|
1328
1251
|
*allNamedSchemaElement(): Generator<NamedSchemaElement<any, any, any>, void, undefined> {
|
|
@@ -1374,6 +1297,7 @@ export class Schema {
|
|
|
1374
1297
|
|
|
1375
1298
|
invalidate() {
|
|
1376
1299
|
this.isValidated = false;
|
|
1300
|
+
this.blueprint.onInvalidation(this);
|
|
1377
1301
|
}
|
|
1378
1302
|
|
|
1379
1303
|
validate() {
|
|
@@ -1381,22 +1305,20 @@ export class Schema {
|
|
|
1381
1305
|
return;
|
|
1382
1306
|
}
|
|
1383
1307
|
|
|
1384
|
-
// This needs to run _before_ graphQL validation: otherwise, the _Entity union will be empty and fail validation.
|
|
1385
1308
|
this.runWithBuiltInModificationAllowed(() => {
|
|
1386
|
-
this.builtIns.prepareValidation(this);
|
|
1387
1309
|
addIntrospectionFields(this);
|
|
1388
1310
|
});
|
|
1389
1311
|
|
|
1390
|
-
// TODO: we
|
|
1391
|
-
// and
|
|
1392
|
-
let errors = validateSDL(this.toAST(), undefined, this.
|
|
1312
|
+
// TODO: we check that all types are properly set (aren't undefined) in `validateSchema`, but `validateSDL` will error out beforehand. We should
|
|
1313
|
+
// probably extract that part of `validateSchema` and run `validateSDL` conditionally on that first check.
|
|
1314
|
+
let errors = validateSDL(this.toAST(), undefined, this.blueprint.validationRules());
|
|
1393
1315
|
errors = errors.concat(validateSchema(this));
|
|
1394
1316
|
|
|
1395
1317
|
// We avoid adding federation-specific validations if the base schema is not proper graphQL as the later can easily trigger
|
|
1396
1318
|
// the former (for instance, someone mistyping the 'fields' argument name of a @key).
|
|
1397
1319
|
if (errors.length === 0) {
|
|
1398
1320
|
this.runWithBuiltInModificationAllowed(() => {
|
|
1399
|
-
errors = this.
|
|
1321
|
+
errors = this.blueprint.onValidation(this);
|
|
1400
1322
|
});
|
|
1401
1323
|
}
|
|
1402
1324
|
|
|
@@ -1407,8 +1329,8 @@ export class Schema {
|
|
|
1407
1329
|
this.isValidated = true;
|
|
1408
1330
|
}
|
|
1409
1331
|
|
|
1410
|
-
clone(builtIns?:
|
|
1411
|
-
const cloned = new Schema(builtIns ?? this.
|
|
1332
|
+
clone(builtIns?: SchemaBlueprint): Schema {
|
|
1333
|
+
const cloned = new Schema(builtIns ?? this.blueprint);
|
|
1412
1334
|
copy(this, cloned);
|
|
1413
1335
|
if (this.isValidated) {
|
|
1414
1336
|
// TODO: when we do actual validation, no point in redoing it, but we should
|
|
@@ -1417,6 +1339,31 @@ export class Schema {
|
|
|
1417
1339
|
}
|
|
1418
1340
|
return cloned;
|
|
1419
1341
|
}
|
|
1342
|
+
|
|
1343
|
+
private getBuiltInDirective<TApplicationArgs extends {[key: string]: any}>(
|
|
1344
|
+
schema: Schema,
|
|
1345
|
+
name: string
|
|
1346
|
+
): DirectiveDefinition<TApplicationArgs> {
|
|
1347
|
+
const directive = schema.directive(name);
|
|
1348
|
+
assert(directive, `The provided schema has not be built with the ${name} directive built-in`);
|
|
1349
|
+
return directive as DirectiveDefinition<TApplicationArgs>;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
includeDirective(schema: Schema): DirectiveDefinition<{if: boolean}> {
|
|
1353
|
+
return this.getBuiltInDirective(schema, 'include');
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
skipDirective(schema: Schema): DirectiveDefinition<{if: boolean}> {
|
|
1357
|
+
return this.getBuiltInDirective(schema, 'skip');
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
deprecatedDirective(schema: Schema): DirectiveDefinition<{reason?: string}> {
|
|
1361
|
+
return this.getBuiltInDirective(schema, 'deprecated');
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
specifiedByDirective(schema: Schema): DirectiveDefinition<{url: string}> {
|
|
1365
|
+
return this.getBuiltInDirective(schema, 'specifiedBy');
|
|
1366
|
+
}
|
|
1420
1367
|
}
|
|
1421
1368
|
|
|
1422
1369
|
export class RootType extends BaseExtensionMember<SchemaDefinition> {
|
|
@@ -1451,12 +1398,13 @@ export class SchemaDefinition extends SchemaElement<SchemaDefinition, Schema> {
|
|
|
1451
1398
|
const coreFeatures = schema.coreFeatures;
|
|
1452
1399
|
if (isCoreSpecDirectiveApplication(applied)) {
|
|
1453
1400
|
if (coreFeatures) {
|
|
1454
|
-
throw error(`Invalid duplicate application of
|
|
1401
|
+
throw error(`Invalid duplicate application of @core/@link`);
|
|
1455
1402
|
}
|
|
1456
|
-
const schemaDirective = applied as Directive<SchemaDefinition,
|
|
1403
|
+
const schemaDirective = applied as Directive<SchemaDefinition, CoreOrLinkDirectiveArgs>;
|
|
1457
1404
|
const args = schemaDirective.arguments();
|
|
1458
|
-
const url = FeatureUrl.parse(args.feature);
|
|
1459
|
-
const
|
|
1405
|
+
const url = FeatureUrl.parse((args.url ?? args.feature)!);
|
|
1406
|
+
const imports = extractCoreFeatureImports(schemaDirective);
|
|
1407
|
+
const core = new CoreFeature(url, args.as ?? url.name, schemaDirective, imports, args.for);
|
|
1460
1408
|
Schema.prototype['markAsCoreSchema'].call(schema, core);
|
|
1461
1409
|
} else if (coreFeatures) {
|
|
1462
1410
|
CoreFeatures.prototype['maybeAddFeature'].call(coreFeatures, applied);
|
|
@@ -1551,6 +1499,10 @@ export class ScalarType extends BaseNamedType<OutputTypeReferencer | InputTypeRe
|
|
|
1551
1499
|
return false; // No inner elements
|
|
1552
1500
|
}
|
|
1553
1501
|
|
|
1502
|
+
protected removeInnerElementsExtensions(): void {
|
|
1503
|
+
// No inner elements
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1554
1506
|
protected removeInnerElements(): void {
|
|
1555
1507
|
// No inner elements
|
|
1556
1508
|
}
|
|
@@ -1657,18 +1609,15 @@ abstract class FieldBasedType<T extends (ObjectType | InterfaceType) & NamedSche
|
|
|
1657
1609
|
/**
|
|
1658
1610
|
* All the fields of this type, excluding the built-in ones.
|
|
1659
1611
|
*/
|
|
1660
|
-
fields(
|
|
1661
|
-
if (includeNonGraphQLBuiltIns) {
|
|
1662
|
-
return this.allFields().filter(f => !graphQLBuiltIns.isGraphQLBuiltIn(f));
|
|
1663
|
-
}
|
|
1612
|
+
fields(): readonly FieldDefinition<T>[] {
|
|
1664
1613
|
if (!this._cachedNonBuiltInFields) {
|
|
1665
1614
|
this._cachedNonBuiltInFields = this._fields.values().filter(f => !f.isBuiltIn);
|
|
1666
1615
|
}
|
|
1667
1616
|
return this._cachedNonBuiltInFields;
|
|
1668
1617
|
}
|
|
1669
1618
|
|
|
1670
|
-
hasFields(
|
|
1671
|
-
return this.fields(
|
|
1619
|
+
hasFields(): boolean {
|
|
1620
|
+
return this.fields().length > 0;
|
|
1672
1621
|
}
|
|
1673
1622
|
|
|
1674
1623
|
/**
|
|
@@ -1761,6 +1710,11 @@ abstract class FieldBasedType<T extends (ObjectType | InterfaceType) & NamedSche
|
|
|
1761
1710
|
return this.interfaceImplementations().some(itf => itf.ofExtension() === undefined)
|
|
1762
1711
|
|| this.fields().some(f => f.ofExtension() === undefined);
|
|
1763
1712
|
}
|
|
1713
|
+
|
|
1714
|
+
protected removeInnerElementsExtensions(): void {
|
|
1715
|
+
this.interfaceImplementations().forEach(itf => itf.removeOfExtension());
|
|
1716
|
+
this.fields().forEach(f => f.removeOfExtension());
|
|
1717
|
+
}
|
|
1764
1718
|
}
|
|
1765
1719
|
|
|
1766
1720
|
export class ObjectType extends FieldBasedType<ObjectType, ObjectTypeReferencer> {
|
|
@@ -1949,6 +1903,10 @@ export class UnionType extends BaseNamedType<OutputTypeReferencer, UnionType> {
|
|
|
1949
1903
|
protected removeReferenceRecursive(ref: OutputTypeReferencer): void {
|
|
1950
1904
|
ref.removeRecursive();
|
|
1951
1905
|
}
|
|
1906
|
+
|
|
1907
|
+
protected removeInnerElementsExtensions(): void {
|
|
1908
|
+
this.members().forEach(m => m.removeOfExtension());
|
|
1909
|
+
}
|
|
1952
1910
|
}
|
|
1953
1911
|
|
|
1954
1912
|
export class EnumType extends BaseNamedType<OutputTypeReferencer, EnumType> {
|
|
@@ -1960,7 +1918,7 @@ export class EnumType extends BaseNamedType<OutputTypeReferencer, EnumType> {
|
|
|
1960
1918
|
}
|
|
1961
1919
|
|
|
1962
1920
|
value(name: string): EnumValue | undefined {
|
|
1963
|
-
return this._values.find(v => v.name
|
|
1921
|
+
return this._values.find(v => v.name === name);
|
|
1964
1922
|
}
|
|
1965
1923
|
|
|
1966
1924
|
addValue(value: EnumValue): EnumValue;
|
|
@@ -2007,6 +1965,10 @@ export class EnumType extends BaseNamedType<OutputTypeReferencer, EnumType> {
|
|
|
2007
1965
|
protected removeReferenceRecursive(ref: OutputTypeReferencer): void {
|
|
2008
1966
|
ref.removeRecursive();
|
|
2009
1967
|
}
|
|
1968
|
+
|
|
1969
|
+
protected removeInnerElementsExtensions(): void {
|
|
1970
|
+
this._values.forEach(v => v.removeOfExtension());
|
|
1971
|
+
}
|
|
2010
1972
|
}
|
|
2011
1973
|
|
|
2012
1974
|
export class InputObjectType extends BaseNamedType<InputTypeReferencer, InputObjectType> {
|
|
@@ -2090,6 +2052,10 @@ export class InputObjectType extends BaseNamedType<InputTypeReferencer, InputObj
|
|
|
2090
2052
|
ref.removeRecursive();
|
|
2091
2053
|
}
|
|
2092
2054
|
}
|
|
2055
|
+
|
|
2056
|
+
protected removeInnerElementsExtensions(): void {
|
|
2057
|
+
this.fields().forEach(f => f.removeOfExtension());
|
|
2058
|
+
}
|
|
2093
2059
|
}
|
|
2094
2060
|
|
|
2095
2061
|
class BaseWrapperType<T extends Type> {
|
|
@@ -2208,6 +2174,10 @@ export class FieldDefinition<TParent extends CompositeType> extends NamedSchemaE
|
|
|
2208
2174
|
return this._extension;
|
|
2209
2175
|
}
|
|
2210
2176
|
|
|
2177
|
+
removeOfExtension() {
|
|
2178
|
+
this._extension = undefined;
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2211
2181
|
setOfExtension(extension: Extension<TParent> | undefined) {
|
|
2212
2182
|
this.checkUpdate();
|
|
2213
2183
|
// It seems typescript "expand" `TParent` below into `ObjectType | Interface`, so it essentially lose the context that
|
|
@@ -2302,6 +2272,10 @@ export class InputFieldDefinition extends NamedSchemaElementWithType<InputType,
|
|
|
2302
2272
|
return this._extension;
|
|
2303
2273
|
}
|
|
2304
2274
|
|
|
2275
|
+
removeOfExtension() {
|
|
2276
|
+
this._extension = undefined;
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2305
2279
|
setOfExtension(extension: Extension<InputObjectType> | undefined) {
|
|
2306
2280
|
this.checkUpdate();
|
|
2307
2281
|
// It seems typescript "expand" `TParent` below into `ObjectType | Interface`, so it essentially lose the context that
|
|
@@ -2414,6 +2388,10 @@ export class EnumValue extends NamedSchemaElement<EnumValue, EnumType, never> {
|
|
|
2414
2388
|
return this._extension;
|
|
2415
2389
|
}
|
|
2416
2390
|
|
|
2391
|
+
removeOfExtension() {
|
|
2392
|
+
this._extension = undefined;
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2417
2395
|
setOfExtension(extension: Extension<EnumType> | undefined) {
|
|
2418
2396
|
this.checkUpdate();
|
|
2419
2397
|
if (extension && !this._parent?.extensions().has(extension)) {
|
|
@@ -2622,6 +2600,9 @@ export class Directive<
|
|
|
2622
2600
|
}
|
|
2623
2601
|
|
|
2624
2602
|
get definition(): DirectiveDefinition | undefined {
|
|
2603
|
+
if (!this.isAttached()) {
|
|
2604
|
+
return undefined;
|
|
2605
|
+
}
|
|
2625
2606
|
const doc = this.schema();
|
|
2626
2607
|
return doc.directive(this.name);
|
|
2627
2608
|
}
|
|
@@ -2681,6 +2662,10 @@ export class Directive<
|
|
|
2681
2662
|
return this._extension;
|
|
2682
2663
|
}
|
|
2683
2664
|
|
|
2665
|
+
removeOfExtension() {
|
|
2666
|
+
this._extension = undefined;
|
|
2667
|
+
}
|
|
2668
|
+
|
|
2684
2669
|
setOfExtension(extension: Extension<any> | undefined) {
|
|
2685
2670
|
this.checkUpdate();
|
|
2686
2671
|
if (extension) {
|
|
@@ -2933,8 +2918,6 @@ export function variableDefinitionFromAST(schema: Schema, definitionNode: Variab
|
|
|
2933
2918
|
return def;
|
|
2934
2919
|
}
|
|
2935
2920
|
|
|
2936
|
-
export const graphQLBuiltIns = new BuiltIns();
|
|
2937
|
-
|
|
2938
2921
|
function addReferenceToType(referencer: SchemaElement<any, any>, type: Type) {
|
|
2939
2922
|
switch (type.kind) {
|
|
2940
2923
|
case 'ListType':
|
|
@@ -3043,7 +3026,7 @@ function copySchemaDefinitionInner(source: SchemaDefinition, dest: SchemaDefinit
|
|
|
3043
3026
|
// Same as copyAppliedDirectives, but as the directive applies to the schema definition, we need to remember if the application
|
|
3044
3027
|
// is for the extension or not.
|
|
3045
3028
|
for (const directive of source.appliedDirectives) {
|
|
3046
|
-
copyOfExtension(extensionsMap, directive,
|
|
3029
|
+
copyOfExtension(extensionsMap, directive, copyAppliedDirective(directive, dest));
|
|
3047
3030
|
}
|
|
3048
3031
|
dest.description = source.description;
|
|
3049
3032
|
dest.sourceAST = source.sourceAST;
|
|
@@ -3054,7 +3037,7 @@ function copyNamedTypeInner(source: NamedType, dest: NamedType) {
|
|
|
3054
3037
|
// Same as copyAppliedDirectives, but as the directive applies to the type, we need to remember if the application
|
|
3055
3038
|
// is for the extension or not.
|
|
3056
3039
|
for (const directive of source.appliedDirectives) {
|
|
3057
|
-
copyOfExtension(extensionsMap, directive,
|
|
3040
|
+
copyOfExtension(extensionsMap, directive, copyAppliedDirective(directive, dest));
|
|
3058
3041
|
}
|
|
3059
3042
|
dest.description = source.description;
|
|
3060
3043
|
dest.sourceAST = source.sourceAST;
|
|
@@ -3099,9 +3082,13 @@ function copyNamedTypeInner(source: NamedType, dest: NamedType) {
|
|
|
3099
3082
|
}
|
|
3100
3083
|
|
|
3101
3084
|
function copyAppliedDirectives(source: SchemaElement<any, any>, dest: SchemaElement<any, any>) {
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3085
|
+
source.appliedDirectives.forEach((d) => copyAppliedDirective(d, dest));
|
|
3086
|
+
}
|
|
3087
|
+
|
|
3088
|
+
function copyAppliedDirective(source: Directive<any, any>, dest: SchemaElement<any, any>): Directive<any, any> {
|
|
3089
|
+
const res = dest.applyDirective(source.name, { ...source.arguments() });
|
|
3090
|
+
res.sourceAST = source.sourceAST
|
|
3091
|
+
return res;
|
|
3105
3092
|
}
|
|
3106
3093
|
|
|
3107
3094
|
function copyFieldDefinitionInner<P extends ObjectType | InterfaceType>(source: FieldDefinition<P>, dest: FieldDefinition<P>) {
|