@apollo/federation-internals 2.0.0-preview.8 → 2.0.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/CHANGELOG.md +35 -0
- package/dist/buildSchema.js +1 -1
- package/dist/buildSchema.js.map +1 -1
- package/dist/coreSpec.d.ts +13 -6
- package/dist/coreSpec.d.ts.map +1 -1
- package/dist/coreSpec.js +105 -38
- package/dist/coreSpec.js.map +1 -1
- package/dist/definitions.d.ts +25 -11
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +115 -63
- package/dist/definitions.js.map +1 -1
- package/dist/directiveAndTypeSpecification.d.ts +11 -1
- package/dist/directiveAndTypeSpecification.d.ts.map +1 -1
- package/dist/directiveAndTypeSpecification.js +77 -20
- package/dist/directiveAndTypeSpecification.js.map +1 -1
- package/dist/error.d.ts +13 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +28 -2
- package/dist/error.js.map +1 -1
- package/dist/extractSubgraphsFromSupergraph.d.ts.map +1 -1
- package/dist/extractSubgraphsFromSupergraph.js +7 -1
- package/dist/extractSubgraphsFromSupergraph.js.map +1 -1
- package/dist/federation.d.ts +19 -6
- package/dist/federation.d.ts.map +1 -1
- package/dist/federation.js +107 -80
- package/dist/federation.js.map +1 -1
- package/dist/federationSpec.d.ts +3 -3
- package/dist/federationSpec.d.ts.map +1 -1
- package/dist/federationSpec.js +34 -26
- package/dist/federationSpec.js.map +1 -1
- package/dist/inaccessibleSpec.d.ts +11 -5
- package/dist/inaccessibleSpec.d.ts.map +1 -1
- package/dist/inaccessibleSpec.js +631 -29
- package/dist/inaccessibleSpec.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/introspection.d.ts.map +1 -1
- package/dist/introspection.js +8 -3
- package/dist/introspection.js.map +1 -1
- package/dist/joinSpec.d.ts +6 -2
- package/dist/joinSpec.d.ts.map +1 -1
- package/dist/joinSpec.js +6 -0
- package/dist/joinSpec.js.map +1 -1
- package/dist/operations.d.ts +1 -0
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +16 -1
- package/dist/operations.js.map +1 -1
- package/dist/{sharing.d.ts → precompute.d.ts} +1 -1
- package/dist/precompute.d.ts.map +1 -0
- package/dist/{sharing.js → precompute.js} +3 -3
- package/dist/precompute.js.map +1 -0
- package/dist/schemaUpgrader.d.ts.map +1 -1
- package/dist/schemaUpgrader.js +17 -7
- package/dist/schemaUpgrader.js.map +1 -1
- package/dist/suggestions.d.ts +1 -1
- package/dist/suggestions.d.ts.map +1 -1
- package/dist/suggestions.js.map +1 -1
- package/dist/supergraphs.d.ts.map +1 -1
- package/dist/supergraphs.js +2 -0
- package/dist/supergraphs.js.map +1 -1
- package/dist/tagSpec.d.ts +6 -2
- package/dist/tagSpec.d.ts.map +1 -1
- package/dist/tagSpec.js +30 -14
- package/dist/tagSpec.js.map +1 -1
- package/dist/validate.js +13 -7
- package/dist/validate.js.map +1 -1
- package/dist/values.d.ts +2 -2
- package/dist/values.d.ts.map +1 -1
- package/dist/values.js +13 -11
- package/dist/values.js.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/coreSpec.test.ts +112 -0
- package/src/__tests__/removeInaccessibleElements.test.ts +2229 -137
- package/src/__tests__/subgraphValidation.test.ts +357 -1
- package/src/__tests__/values.test.ts +315 -3
- package/src/buildSchema.ts +1 -1
- package/src/coreSpec.ts +161 -48
- package/src/definitions.ts +223 -90
- package/src/directiveAndTypeSpecification.ts +98 -21
- package/src/error.ts +80 -2
- package/src/extractSubgraphsFromSupergraph.ts +7 -1
- package/src/federation.ts +124 -97
- package/src/federationSpec.ts +37 -30
- package/src/inaccessibleSpec.ts +983 -49
- package/src/index.ts +1 -0
- package/src/introspection.ts +8 -3
- package/src/joinSpec.ts +19 -4
- package/src/operations.ts +15 -0
- package/src/{sharing.ts → precompute.ts} +3 -6
- package/src/schemaUpgrader.ts +29 -13
- package/src/suggestions.ts +1 -1
- package/src/supergraphs.ts +2 -0
- package/src/tagSpec.ts +42 -16
- package/src/validate.ts +20 -9
- package/src/values.ts +39 -12
- package/tsconfig.test.tsbuildinfo +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/sharing.d.ts.map +0 -1
- package/dist/sharing.js.map +0 -1
package/src/definitions.ts
CHANGED
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
FeatureUrl,
|
|
25
25
|
findCoreSpecVersion,
|
|
26
26
|
isCoreSpecDirectiveApplication,
|
|
27
|
-
|
|
27
|
+
removeAllCoreFeatures,
|
|
28
28
|
} from "./coreSpec";
|
|
29
29
|
import { assert, mapValues, MapWithCachedArrays, setValues } from "./utils";
|
|
30
30
|
import { withDefaultValues, valueEquals, valueToString, valueToAST, variablesInValue, valueFromAST, valueNodeToConstValueNode } from "./values";
|
|
@@ -43,10 +43,19 @@ import { didYouMean, suggestionList } from "./suggestions";
|
|
|
43
43
|
import { withModifiedErrorMessage } from "./error";
|
|
44
44
|
|
|
45
45
|
const validationErrorCode = 'GraphQLValidationFailed';
|
|
46
|
+
const DEFAULT_VALIDATION_ERROR_MESSAGE = 'The schema is not a valid GraphQL schema.';
|
|
46
47
|
|
|
47
|
-
export const ErrGraphQLValidationFailed = (causes: GraphQLError[]) =>
|
|
48
|
+
export const ErrGraphQLValidationFailed = (causes: GraphQLError[], message: string = DEFAULT_VALIDATION_ERROR_MESSAGE) =>
|
|
48
49
|
err(validationErrorCode, {
|
|
49
|
-
message: '
|
|
50
|
+
message: message + '. Caused by:\n' + causes.map((c) => c.toString()).join('\n\n'),
|
|
51
|
+
causes
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const apiSchemaValidationErrorCode = 'GraphQLAPISchemaValidationFailed';
|
|
55
|
+
|
|
56
|
+
export const ErrGraphQLAPISchemaValidationFailed = (causes: GraphQLError[]) =>
|
|
57
|
+
err(apiSchemaValidationErrorCode, {
|
|
58
|
+
message: 'The supergraph schema failed to produce a valid API schema',
|
|
50
59
|
causes
|
|
51
60
|
});
|
|
52
61
|
|
|
@@ -56,7 +65,7 @@ export const ErrGraphQLValidationFailed = (causes: GraphQLError[]) =>
|
|
|
56
65
|
*/
|
|
57
66
|
export function errorCauses(e: Error): GraphQLError[] | undefined {
|
|
58
67
|
if (e instanceof GraphQLErrorExt) {
|
|
59
|
-
if (e.code === validationErrorCode) {
|
|
68
|
+
if (e.code === validationErrorCode || e.code === apiSchemaValidationErrorCode) {
|
|
60
69
|
return ((e as any).causes) as GraphQLError[];
|
|
61
70
|
}
|
|
62
71
|
return [e];
|
|
@@ -213,6 +222,22 @@ export function isInputType(type: Type): type is InputType {
|
|
|
213
222
|
}
|
|
214
223
|
}
|
|
215
224
|
|
|
225
|
+
export function isTypeOfKind<T extends Type>(type: Type, kind: T['kind']): type is T {
|
|
226
|
+
return type.kind === kind;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function filterTypesOfKind<T extends Type>(types: readonly Type[], kind: T['kind']): T[] {
|
|
230
|
+
return types.reduce(
|
|
231
|
+
(acc: T[], type: Type) => {
|
|
232
|
+
if (isTypeOfKind(type, kind)) {
|
|
233
|
+
acc.push(type);
|
|
234
|
+
}
|
|
235
|
+
return acc;
|
|
236
|
+
},
|
|
237
|
+
[],
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
216
241
|
export function baseType(type: Type): NamedType {
|
|
217
242
|
return isWrapperType(type) ? type.baseType() : type;
|
|
218
243
|
}
|
|
@@ -382,8 +407,8 @@ export class DirectiveTargetElement<T extends DirectiveTargetElement<T>> {
|
|
|
382
407
|
}
|
|
383
408
|
}
|
|
384
409
|
|
|
385
|
-
export function sourceASTs(...elts: ({ sourceAST?:
|
|
386
|
-
return elts.map(elt => elt?.sourceAST).filter(elt => elt !== undefined)
|
|
410
|
+
export function sourceASTs<TNode extends ASTNode = ASTNode>(...elts: ({ sourceAST?: TNode } | undefined)[]): TNode[] {
|
|
411
|
+
return elts.map(elt => elt?.sourceAST).filter((elt): elt is TNode => elt !== undefined);
|
|
387
412
|
}
|
|
388
413
|
|
|
389
414
|
// Not exposed: mostly about avoid code duplication between SchemaElement and Directive (which is not a SchemaElement as it can't
|
|
@@ -491,37 +516,36 @@ export abstract class SchemaElement<TOwnType extends SchemaElement<any, TParent>
|
|
|
491
516
|
}
|
|
492
517
|
|
|
493
518
|
applyDirective<TApplicationArgs extends {[key: string]: any} = {[key: string]: any}>(
|
|
494
|
-
|
|
495
|
-
args?: TApplicationArgs
|
|
519
|
+
nameOrDef: DirectiveDefinition<TApplicationArgs> | string,
|
|
520
|
+
args?: TApplicationArgs,
|
|
521
|
+
asFirstDirective: boolean = false,
|
|
496
522
|
): Directive<TOwnType, TApplicationArgs> {
|
|
497
|
-
let
|
|
498
|
-
if (
|
|
499
|
-
this.checkUpdate(
|
|
500
|
-
|
|
501
|
-
if (
|
|
502
|
-
|
|
523
|
+
let name: string;
|
|
524
|
+
if (typeof nameOrDef === 'string') {
|
|
525
|
+
this.checkUpdate();
|
|
526
|
+
const def = this.schema().directive(nameOrDef) ?? this.schema().blueprint.onMissingDirectiveDefinition(this.schema(), nameOrDef, args);
|
|
527
|
+
if (!def) {
|
|
528
|
+
throw this.schema().blueprint.onGraphQLJSValidationError(
|
|
529
|
+
this.schema(),
|
|
530
|
+
new GraphQLError(`Unknown directive "@${nameOrDef}".`)
|
|
531
|
+
);
|
|
503
532
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
if (typeof nameOrDefOrDirective === 'string') {
|
|
507
|
-
this.checkUpdate();
|
|
508
|
-
const def = this.schema().directive(nameOrDefOrDirective) ?? this.schema().blueprint.onMissingDirectiveDefinition(this.schema(), nameOrDefOrDirective);
|
|
509
|
-
if (!def) {
|
|
510
|
-
throw this.schema().blueprint.onGraphQLJSValidationError(
|
|
511
|
-
this.schema(),
|
|
512
|
-
new GraphQLError(`Unknown directive "@${nameOrDefOrDirective}".`)
|
|
513
|
-
);
|
|
514
|
-
}
|
|
515
|
-
name = nameOrDefOrDirective;
|
|
516
|
-
} else {
|
|
517
|
-
this.checkUpdate(nameOrDefOrDirective);
|
|
518
|
-
name = nameOrDefOrDirective.name;
|
|
533
|
+
if (Array.isArray(def)) {
|
|
534
|
+
throw ErrGraphQLValidationFailed(def);
|
|
519
535
|
}
|
|
520
|
-
|
|
521
|
-
|
|
536
|
+
name = nameOrDef;
|
|
537
|
+
} else {
|
|
538
|
+
this.checkUpdate(nameOrDef);
|
|
539
|
+
name = nameOrDef.name;
|
|
522
540
|
}
|
|
541
|
+
const toAdd = new Directive<TOwnType, TApplicationArgs>(name, args ?? Object.create(null));
|
|
542
|
+
Element.prototype['setParent'].call(toAdd, this);
|
|
523
543
|
// TODO: we should typecheck arguments or our TApplicationArgs business is just a lie.
|
|
524
|
-
|
|
544
|
+
if (asFirstDirective) {
|
|
545
|
+
this._appliedDirectives.unshift(toAdd);
|
|
546
|
+
} else {
|
|
547
|
+
this._appliedDirectives.push(toAdd);
|
|
548
|
+
}
|
|
525
549
|
DirectiveDefinition.prototype['addReferencer'].call(toAdd.definition!, toAdd);
|
|
526
550
|
this.onModification();
|
|
527
551
|
return toAdd;
|
|
@@ -699,7 +723,7 @@ abstract class BaseNamedType<TReferencer, TOwnType extends NamedType & NamedSche
|
|
|
699
723
|
/**
|
|
700
724
|
* Removes this type definition from its parent schema.
|
|
701
725
|
*
|
|
702
|
-
* After calling this method, this type will be "detached": it
|
|
726
|
+
* After calling this method, this type will be "detached": it will have no parent, schema, fields,
|
|
703
727
|
* values, directives, etc...
|
|
704
728
|
*
|
|
705
729
|
* Note that it is always allowed to remove a type, but this may make a valid schema
|
|
@@ -715,15 +739,18 @@ abstract class BaseNamedType<TReferencer, TOwnType extends NamedType & NamedSche
|
|
|
715
739
|
}
|
|
716
740
|
this.checkRemoval();
|
|
717
741
|
this.onModification();
|
|
718
|
-
this.
|
|
719
|
-
Schema.prototype['removeTypeInternal'].call(this._parent, this);
|
|
720
|
-
this.removeAppliedDirectives();
|
|
742
|
+
// Remove this type's children.
|
|
721
743
|
this.sourceAST = undefined;
|
|
744
|
+
this.removeAppliedDirectives();
|
|
745
|
+
this.removeInnerElements();
|
|
746
|
+
// Remove this type's references.
|
|
722
747
|
const toReturn = setValues(this._referencers).map(r => {
|
|
723
748
|
SchemaElement.prototype['removeTypeReferenceInternal'].call(r, this);
|
|
724
749
|
return r;
|
|
725
750
|
});
|
|
726
751
|
this._referencers.clear();
|
|
752
|
+
// Remove this type from its parent schema.
|
|
753
|
+
Schema.prototype['removeTypeInternal'].call(this._parent, this);
|
|
727
754
|
this._parent = undefined;
|
|
728
755
|
return toReturn;
|
|
729
756
|
}
|
|
@@ -828,13 +855,14 @@ abstract class BaseExtensionMember<TExtended extends ExtendableElement> extends
|
|
|
828
855
|
}
|
|
829
856
|
|
|
830
857
|
export class SchemaBlueprint {
|
|
831
|
-
onMissingDirectiveDefinition(_schema: Schema, _name: string): DirectiveDefinition | undefined {
|
|
858
|
+
onMissingDirectiveDefinition(_schema: Schema, _name: string, _args?: {[key: string]: any}): DirectiveDefinition | GraphQLError[] | undefined {
|
|
832
859
|
// No-op by default, but used for federation.
|
|
833
860
|
return undefined;
|
|
834
861
|
}
|
|
835
862
|
|
|
836
|
-
onDirectiveDefinitionAndSchemaParsed(_: Schema) {
|
|
863
|
+
onDirectiveDefinitionAndSchemaParsed(_: Schema): GraphQLError[] {
|
|
837
864
|
// No-op by default, but used for federation.
|
|
865
|
+
return [];
|
|
838
866
|
}
|
|
839
867
|
|
|
840
868
|
ignoreParsedField(_type: NamedType, _fieldName: string): boolean {
|
|
@@ -909,9 +937,12 @@ export class CoreFeature {
|
|
|
909
937
|
}
|
|
910
938
|
|
|
911
939
|
isFeatureDefinition(element: NamedType | DirectiveDefinition): boolean {
|
|
940
|
+
const importName = element.kind === 'DirectiveDefinition'
|
|
941
|
+
? '@' + element.name
|
|
942
|
+
: element.name;
|
|
912
943
|
return element.name.startsWith(this.nameInSchema + '__')
|
|
913
944
|
|| (element.kind === 'DirectiveDefinition' && element.name === this.nameInSchema)
|
|
914
|
-
|| !!this.imports.find((i) =>
|
|
945
|
+
|| !!this.imports.find((i) => importName === (i.as ?? i.name));
|
|
915
946
|
}
|
|
916
947
|
|
|
917
948
|
directiveNameInSchema(name: string): string {
|
|
@@ -1014,22 +1045,22 @@ const graphQLBuiltInDirectivesSpecifications: readonly DirectiveSpecification[]
|
|
|
1014
1045
|
createDirectiveSpecification({
|
|
1015
1046
|
name: 'include',
|
|
1016
1047
|
locations: [DirectiveLocation.FIELD, DirectiveLocation.FRAGMENT_SPREAD, DirectiveLocation.INLINE_FRAGMENT],
|
|
1017
|
-
argumentFct: (schema) => [{ name: 'if', type: new NonNullType(schema.booleanType()) }]
|
|
1048
|
+
argumentFct: (schema) => ({ args: [{ name: 'if', type: new NonNullType(schema.booleanType()) }], errors: [] })
|
|
1018
1049
|
}),
|
|
1019
1050
|
createDirectiveSpecification({
|
|
1020
1051
|
name: 'skip',
|
|
1021
1052
|
locations: [DirectiveLocation.FIELD, DirectiveLocation.FRAGMENT_SPREAD, DirectiveLocation.INLINE_FRAGMENT],
|
|
1022
|
-
argumentFct: (schema) => [{ name: 'if', type: new NonNullType(schema.booleanType()) }]
|
|
1053
|
+
argumentFct: (schema) => ({ args: [{ name: 'if', type: new NonNullType(schema.booleanType()) }], errors: [] })
|
|
1023
1054
|
}),
|
|
1024
1055
|
createDirectiveSpecification({
|
|
1025
1056
|
name: 'deprecated',
|
|
1026
1057
|
locations: [DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.ENUM_VALUE, DirectiveLocation.ARGUMENT_DEFINITION, DirectiveLocation.INPUT_FIELD_DEFINITION],
|
|
1027
|
-
argumentFct: (schema) => [{ name: 'reason', type: schema.stringType(), defaultValue: 'No longer supported' }]
|
|
1058
|
+
argumentFct: (schema) => ({ args: [{ name: 'reason', type: schema.stringType(), defaultValue: 'No longer supported' }], errors: [] })
|
|
1028
1059
|
}),
|
|
1029
1060
|
createDirectiveSpecification({
|
|
1030
1061
|
name: 'specifiedBy',
|
|
1031
1062
|
locations: [DirectiveLocation.SCALAR],
|
|
1032
|
-
argumentFct: (schema) => [{ name: 'url', type: new NonNullType(schema.stringType()) }]
|
|
1063
|
+
argumentFct: (schema) => ({ args: [{ name: 'url', type: new NonNullType(schema.stringType()) }], errors: [] })
|
|
1033
1064
|
}),
|
|
1034
1065
|
];
|
|
1035
1066
|
|
|
@@ -1126,13 +1157,7 @@ export class Schema {
|
|
|
1126
1157
|
|
|
1127
1158
|
const apiSchema = this.clone();
|
|
1128
1159
|
removeInaccessibleElements(apiSchema);
|
|
1129
|
-
|
|
1130
|
-
if (coreFeatures) {
|
|
1131
|
-
// Note that core being a feature itself, this will remove core itself and mark apiSchema as 'not core'
|
|
1132
|
-
for (const coreFeature of coreFeatures.allFeatures()) {
|
|
1133
|
-
removeFeatureElements(apiSchema, coreFeature);
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1160
|
+
removeAllCoreFeatures(apiSchema);
|
|
1136
1161
|
assert(!apiSchema.isCoreSchema(), "The API schema shouldn't be a core schema")
|
|
1137
1162
|
apiSchema.validate();
|
|
1138
1163
|
this.apiSchema = apiSchema;
|
|
@@ -1160,20 +1185,42 @@ export class Schema {
|
|
|
1160
1185
|
/**
|
|
1161
1186
|
* All the types defined on this schema, excluding the built-in types.
|
|
1162
1187
|
*/
|
|
1163
|
-
types
|
|
1164
|
-
|
|
1165
|
-
|
|
1188
|
+
types(): readonly NamedType[] {
|
|
1189
|
+
return this._types.values();
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
interfaceTypes(): readonly InterfaceType[] {
|
|
1193
|
+
return filterTypesOfKind<InterfaceType>(this.types(), 'InterfaceType');
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
objectTypes(): readonly ObjectType[] {
|
|
1197
|
+
return filterTypesOfKind<ObjectType>(this.types(), 'ObjectType');
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
unionTypes(): readonly UnionType[] {
|
|
1201
|
+
return filterTypesOfKind<UnionType>(this.types(), 'UnionType');
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
scalarTypes(): readonly ScalarType[] {
|
|
1205
|
+
return filterTypesOfKind<ScalarType>(this.types(), 'ScalarType');
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
inputTypes(): readonly InputObjectType[] {
|
|
1209
|
+
return filterTypesOfKind<InputObjectType>(this.types(), 'InputObjectType');
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
enumTypes(): readonly EnumType[] {
|
|
1213
|
+
return filterTypesOfKind<EnumType>(this.types(), 'EnumType');
|
|
1166
1214
|
}
|
|
1167
1215
|
|
|
1168
1216
|
/**
|
|
1169
1217
|
* All the built-in types for this schema (those that are not displayed when printing the schema).
|
|
1170
1218
|
*/
|
|
1171
|
-
builtInTypes
|
|
1219
|
+
builtInTypes(includeShadowed: boolean = false): readonly NamedType[] {
|
|
1172
1220
|
const allBuiltIns = this._builtInTypes.values();
|
|
1173
|
-
const forKind = (kind ? allBuiltIns.filter(t => t.kind === kind) : allBuiltIns) as readonly T[];
|
|
1174
1221
|
return includeShadowed
|
|
1175
|
-
?
|
|
1176
|
-
:
|
|
1222
|
+
? allBuiltIns
|
|
1223
|
+
: allBuiltIns.filter(t => !this.isShadowedBuiltInType(t));
|
|
1177
1224
|
}
|
|
1178
1225
|
|
|
1179
1226
|
private isShadowedBuiltInType(type: NamedType) {
|
|
@@ -1183,8 +1230,8 @@ export class Schema {
|
|
|
1183
1230
|
/**
|
|
1184
1231
|
* All the types, including the built-in ones.
|
|
1185
1232
|
*/
|
|
1186
|
-
allTypes
|
|
1187
|
-
return this.builtInTypes(
|
|
1233
|
+
allTypes(): readonly NamedType[] {
|
|
1234
|
+
return this.builtInTypes().concat(this.types());
|
|
1188
1235
|
}
|
|
1189
1236
|
|
|
1190
1237
|
/**
|
|
@@ -1482,10 +1529,11 @@ export class SchemaDefinition extends SchemaElement<SchemaDefinition, Schema> {
|
|
|
1482
1529
|
}
|
|
1483
1530
|
|
|
1484
1531
|
applyDirective<TApplicationArgs extends {[key: string]: any} = {[key: string]: any}>(
|
|
1485
|
-
|
|
1486
|
-
args?: TApplicationArgs
|
|
1532
|
+
nameOrDef: DirectiveDefinition<TApplicationArgs> | string,
|
|
1533
|
+
args?: TApplicationArgs,
|
|
1534
|
+
asFirstDirective: boolean = false,
|
|
1487
1535
|
): Directive<SchemaDefinition, TApplicationArgs> {
|
|
1488
|
-
const applied = super.applyDirective(
|
|
1536
|
+
const applied = super.applyDirective(nameOrDef, args, asFirstDirective) as Directive<SchemaDefinition, TApplicationArgs>;
|
|
1489
1537
|
const schema = this.schema();
|
|
1490
1538
|
const coreFeatures = schema.coreFeatures;
|
|
1491
1539
|
if (isCoreSpecDirectiveApplication(applied)) {
|
|
@@ -1498,6 +1546,10 @@ export class SchemaDefinition extends SchemaElement<SchemaDefinition, Schema> {
|
|
|
1498
1546
|
const imports = extractCoreFeatureImports(url, schemaDirective);
|
|
1499
1547
|
const core = new CoreFeature(url, args.as ?? url.name, schemaDirective, imports, args.for);
|
|
1500
1548
|
Schema.prototype['markAsCoreSchema'].call(schema, core);
|
|
1549
|
+
// We also any core features that may have been added before we saw the @link for link itself
|
|
1550
|
+
this.appliedDirectives
|
|
1551
|
+
.filter((a) => a !== applied)
|
|
1552
|
+
.forEach((other) => CoreFeatures.prototype['maybeAddFeature'].call(schema.coreFeatures, other));
|
|
1501
1553
|
} else if (coreFeatures) {
|
|
1502
1554
|
CoreFeatures.prototype['maybeAddFeature'].call(coreFeatures, applied);
|
|
1503
1555
|
}
|
|
@@ -2006,7 +2058,12 @@ export class EnumType extends BaseNamedType<OutputTypeReferencer, EnumType> {
|
|
|
2006
2058
|
protected readonly _values: EnumValue[] = [];
|
|
2007
2059
|
|
|
2008
2060
|
get values(): readonly EnumValue[] {
|
|
2009
|
-
|
|
2061
|
+
// Because our abstractions are mutable, and removal is done by calling
|
|
2062
|
+
// `remove()` on the element to remove, it's not unlikely someone mauy
|
|
2063
|
+
// try to iterate on the result of this method and call `remove()` on
|
|
2064
|
+
// some of the return value based on some condition. But this will break
|
|
2065
|
+
// in an error-prone way if we don't copy, so we do.
|
|
2066
|
+
return Array.from(this._values);
|
|
2010
2067
|
}
|
|
2011
2068
|
|
|
2012
2069
|
value(name: string): EnumValue | undefined {
|
|
@@ -2305,23 +2362,36 @@ export class FieldDefinition<TParent extends CompositeType> extends NamedSchemaE
|
|
|
2305
2362
|
/**
|
|
2306
2363
|
* Removes this field definition from its parent type.
|
|
2307
2364
|
*
|
|
2308
|
-
* After calling this method, this field definition will be "detached": it
|
|
2309
|
-
* arguments or directives.
|
|
2365
|
+
* After calling this method, this field definition will be "detached": it will have no parent, schema, type,
|
|
2366
|
+
* arguments, or directives.
|
|
2310
2367
|
*/
|
|
2311
2368
|
remove(): never[] {
|
|
2312
2369
|
if (!this._parent) {
|
|
2313
2370
|
return [];
|
|
2314
2371
|
}
|
|
2372
|
+
this.checkRemoval();
|
|
2315
2373
|
this.onModification();
|
|
2316
|
-
this.
|
|
2374
|
+
// Remove this field's children.
|
|
2375
|
+
this.sourceAST = undefined;
|
|
2317
2376
|
this.type = undefined;
|
|
2318
|
-
this.
|
|
2377
|
+
this.removeAppliedDirectives();
|
|
2319
2378
|
for (const arg of this.arguments()) {
|
|
2320
2379
|
arg.remove();
|
|
2321
2380
|
}
|
|
2381
|
+
// Note that we don't track field references outside of parents, so no
|
|
2382
|
+
// removal needed there.
|
|
2383
|
+
//
|
|
2384
|
+
// TODO: One could consider interface fields as references to implementing
|
|
2385
|
+
// object/interface fields, in the sense that removing an implementing
|
|
2386
|
+
// object/interface field breaks the validity of the implementing
|
|
2387
|
+
// interface field. Being aware that an object/interface field is being
|
|
2388
|
+
// referenced in such a way would be useful for understanding breakages
|
|
2389
|
+
// that need to be resolved as a consequence of removal.
|
|
2390
|
+
//
|
|
2391
|
+
// Remove this field from its parent object/interface type.
|
|
2322
2392
|
FieldBasedType.prototype['removeFieldInternal'].call(this._parent, this);
|
|
2323
2393
|
this._parent = undefined;
|
|
2324
|
-
|
|
2394
|
+
this._extension = undefined;
|
|
2325
2395
|
return [];
|
|
2326
2396
|
}
|
|
2327
2397
|
|
|
@@ -2384,20 +2454,38 @@ export class InputFieldDefinition extends NamedSchemaElementWithType<InputType,
|
|
|
2384
2454
|
}
|
|
2385
2455
|
|
|
2386
2456
|
/**
|
|
2387
|
-
* Removes this field definition from its parent type.
|
|
2457
|
+
* Removes this input field definition from its parent type.
|
|
2388
2458
|
*
|
|
2389
|
-
* After calling this method, this field definition will be "detached": it
|
|
2390
|
-
*
|
|
2459
|
+
* After calling this method, this input field definition will be "detached": it will have no parent, schema,
|
|
2460
|
+
* type, default value, or directives.
|
|
2391
2461
|
*/
|
|
2392
2462
|
remove(): never[] {
|
|
2393
2463
|
if (!this._parent) {
|
|
2394
2464
|
return [];
|
|
2395
2465
|
}
|
|
2466
|
+
this.checkRemoval();
|
|
2396
2467
|
this.onModification();
|
|
2468
|
+
// Remove this input field's children.
|
|
2469
|
+
this.sourceAST = undefined;
|
|
2470
|
+
this.type = undefined;
|
|
2471
|
+
this.defaultValue = undefined;
|
|
2472
|
+
this.removeAppliedDirectives();
|
|
2473
|
+
// Note that we don't track input field references outside of parents, so no
|
|
2474
|
+
// removal needed there.
|
|
2475
|
+
//
|
|
2476
|
+
// TODO: One could consider default values (in field arguments, input
|
|
2477
|
+
// fields, or directive definitions) as references to input fields they
|
|
2478
|
+
// use, in the sense that removing the input field breaks the validity of
|
|
2479
|
+
// the default value. Being aware that an input field is being referenced
|
|
2480
|
+
// in such a way would be useful for understanding breakages that need to
|
|
2481
|
+
// be resolved as a consequence of removal. (The reference is indirect
|
|
2482
|
+
// though, as input field usages are currently represented as strings
|
|
2483
|
+
// within GraphQL values).
|
|
2484
|
+
//
|
|
2485
|
+
// Remove this input field from its parent input object type.
|
|
2397
2486
|
InputObjectType.prototype['removeFieldInternal'].call(this._parent, this);
|
|
2398
2487
|
this._parent = undefined;
|
|
2399
|
-
this.
|
|
2400
|
-
// Fields have nothing that can reference them outside of their parents
|
|
2488
|
+
this._extension = undefined;
|
|
2401
2489
|
return [];
|
|
2402
2490
|
}
|
|
2403
2491
|
|
|
@@ -2442,22 +2530,39 @@ export class ArgumentDefinition<TParent extends FieldDefinition<any> | Directive
|
|
|
2442
2530
|
/**
|
|
2443
2531
|
* Removes this argument definition from its parent element (field or directive).
|
|
2444
2532
|
*
|
|
2445
|
-
* After calling this method, this argument definition will be "detached": it
|
|
2446
|
-
* default value or directives.
|
|
2533
|
+
* After calling this method, this argument definition will be "detached": it will have no parent, schema, type,
|
|
2534
|
+
* default value, or directives.
|
|
2447
2535
|
*/
|
|
2448
2536
|
remove(): never[] {
|
|
2449
2537
|
if (!this._parent) {
|
|
2450
2538
|
return [];
|
|
2451
2539
|
}
|
|
2540
|
+
this.checkRemoval();
|
|
2452
2541
|
this.onModification();
|
|
2542
|
+
// Remove this argument's children.
|
|
2543
|
+
this.sourceAST = undefined;
|
|
2544
|
+
this.type = undefined;
|
|
2545
|
+
this.defaultValue = undefined;
|
|
2546
|
+
this.removeAppliedDirectives();
|
|
2547
|
+
// Note that we don't track argument references outside of parents, so no
|
|
2548
|
+
// removal needed there.
|
|
2549
|
+
//
|
|
2550
|
+
// TODO: One could consider the arguments of directive applications as
|
|
2551
|
+
// references to the arguments of directive definitions, in the sense that
|
|
2552
|
+
// removing a directive definition argument can break the validity of the
|
|
2553
|
+
// directive application. Being aware that a directive definition argument
|
|
2554
|
+
// is being referenced in such a way would be useful for understanding
|
|
2555
|
+
// breakages that need to be resolved as a consequence of removal. (You
|
|
2556
|
+
// could make a similar claim about interface field arguments being
|
|
2557
|
+
// references to object field arguments.)
|
|
2558
|
+
//
|
|
2559
|
+
// Remove this argument from its parent field or directive definition.
|
|
2453
2560
|
if (this._parent instanceof FieldDefinition) {
|
|
2454
2561
|
FieldDefinition.prototype['removeArgumentInternal'].call(this._parent, this.name);
|
|
2455
2562
|
} else {
|
|
2456
2563
|
DirectiveDefinition.prototype['removeArgumentInternal'].call(this._parent, this.name);
|
|
2457
2564
|
}
|
|
2458
2565
|
this._parent = undefined;
|
|
2459
|
-
this.type = undefined;
|
|
2460
|
-
this.defaultValue = undefined;
|
|
2461
2566
|
return [];
|
|
2462
2567
|
}
|
|
2463
2568
|
|
|
@@ -2498,23 +2603,36 @@ export class EnumValue extends NamedSchemaElement<EnumValue, EnumType, never> {
|
|
|
2498
2603
|
}
|
|
2499
2604
|
|
|
2500
2605
|
/**
|
|
2501
|
-
* Removes this
|
|
2606
|
+
* Removes this enum value definition from its parent type.
|
|
2502
2607
|
*
|
|
2503
|
-
* After calling this method, this
|
|
2504
|
-
* arguments or directives.
|
|
2608
|
+
* After calling this method, this enum value definition will be "detached": it will have no parent, schema, type,
|
|
2609
|
+
* arguments, or directives.
|
|
2505
2610
|
*/
|
|
2506
2611
|
remove(): never[] {
|
|
2507
2612
|
if (!this._parent) {
|
|
2508
2613
|
return [];
|
|
2509
2614
|
}
|
|
2615
|
+
this.checkRemoval();
|
|
2510
2616
|
this.onModification();
|
|
2617
|
+
// Remove this enum value's children.
|
|
2618
|
+
this.sourceAST = undefined;
|
|
2619
|
+
this.removeAppliedDirectives();
|
|
2620
|
+
// Note that we don't track enum value references outside of parents, so no
|
|
2621
|
+
// removal needed there.
|
|
2622
|
+
//
|
|
2623
|
+
// TODO: One could consider default values (in field arguments, input
|
|
2624
|
+
// fields, or directive definitions) as references to enum values they
|
|
2625
|
+
// use, in the sense that removing the enum value breaks the validity of
|
|
2626
|
+
// the default value. Being aware that an enum value is being referenced
|
|
2627
|
+
// in such a way would be useful for understanding breakages that need to
|
|
2628
|
+
// be resolved as a consequence of removal. (The reference is indirect
|
|
2629
|
+
// though, as enum value usages are currently represented as strings
|
|
2630
|
+
// within GraphQL values).
|
|
2631
|
+
//
|
|
2632
|
+
// Remove this enum value from its parent enum type.
|
|
2511
2633
|
EnumType.prototype['removeValueInternal'].call(this._parent, this);
|
|
2512
2634
|
this._parent = undefined;
|
|
2513
|
-
|
|
2514
|
-
// TODO: that's actually only semi-true if you include arguments, because default values in args and concrete directive applications can
|
|
2515
|
-
// indirectly refer to enum value. It's indirect though as we currently keep enum value as string in values. That said, it would
|
|
2516
|
-
// probably be really nice to be able to known if an enum value is used or not, rather then removing it and not knowing if we broke
|
|
2517
|
-
// something).
|
|
2635
|
+
this._extension = undefined;
|
|
2518
2636
|
return [];
|
|
2519
2637
|
}
|
|
2520
2638
|
|
|
@@ -2644,22 +2762,35 @@ export class DirectiveDefinition<TApplicationArgs extends {[key: string]: any} =
|
|
|
2644
2762
|
assert(false, `Directive definition ${this} can't reference other types (it's arguments can); shouldn't be asked to remove reference to ${type}`);
|
|
2645
2763
|
}
|
|
2646
2764
|
|
|
2765
|
+
/**
|
|
2766
|
+
* Removes this directive definition from its parent schema.
|
|
2767
|
+
*
|
|
2768
|
+
* After calling this method, this directive definition will be "detached": it will have no parent, schema, or
|
|
2769
|
+
* arguments.
|
|
2770
|
+
*/
|
|
2647
2771
|
remove(): Directive[] {
|
|
2648
2772
|
if (!this._parent) {
|
|
2649
2773
|
return [];
|
|
2650
2774
|
}
|
|
2775
|
+
this.checkRemoval();
|
|
2651
2776
|
this.onModification();
|
|
2652
|
-
|
|
2653
|
-
this.
|
|
2777
|
+
// Remove this directive definition's children.
|
|
2778
|
+
this.sourceAST = undefined;
|
|
2654
2779
|
assert(this._appliedDirectives.length === 0, "Directive definition should not have directive applied to it");
|
|
2655
2780
|
for (const arg of this.arguments()) {
|
|
2656
2781
|
arg.remove();
|
|
2657
2782
|
}
|
|
2658
|
-
//
|
|
2659
|
-
//
|
|
2660
|
-
//
|
|
2783
|
+
// Remove this directive definition's references.
|
|
2784
|
+
//
|
|
2785
|
+
// Note that while a directive application references its definition, it
|
|
2786
|
+
// doesn't store a link to that definition. Instead, we fetch the definition
|
|
2787
|
+
// from the schema when requested. So we don't have to do anything on the
|
|
2788
|
+
// referencers other than clear them (and return the pre-cleared set).
|
|
2661
2789
|
const toReturn = setValues(this._referencers);
|
|
2662
2790
|
this._referencers.clear();
|
|
2791
|
+
// Remove this directive definition from its parent schema.
|
|
2792
|
+
Schema.prototype['removeDirectiveInternal'].call(this._parent, this);
|
|
2793
|
+
this._parent = undefined;
|
|
2663
2794
|
return toReturn;
|
|
2664
2795
|
}
|
|
2665
2796
|
|
|
@@ -2825,10 +2956,12 @@ export class Directive<
|
|
|
2825
2956
|
if (!this._parent) {
|
|
2826
2957
|
return false;
|
|
2827
2958
|
}
|
|
2959
|
+
// Remove this directive application's reference to its definition.
|
|
2828
2960
|
const definition = this.definition;
|
|
2829
2961
|
if (definition && this.isAttachedToSchemaElement()) {
|
|
2830
2962
|
DirectiveDefinition.prototype['removeReferencer'].call(definition, this as Directive<SchemaElement<any, any>>);
|
|
2831
2963
|
}
|
|
2964
|
+
// Remove this directive application from its parent schema element.
|
|
2832
2965
|
const parentDirectives = this._parent.appliedDirectives as Directive<TParent>[];
|
|
2833
2966
|
const index = parentDirectives.indexOf(this);
|
|
2834
2967
|
assert(index >= 0, () => `Directive ${this} lists ${this._parent} as parent, but that parent doesn't list it as applied directive`);
|