@apollo/federation-internals 2.0.0-alpha.1 → 2.0.0-alpha.2
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 +13 -0
- package/dist/buildSchema.js.map +1 -1
- package/dist/coreSpec.d.ts +1 -1
- package/dist/coreSpec.d.ts.map +1 -1
- package/dist/coreSpec.js +2 -0
- package/dist/coreSpec.js.map +1 -1
- package/dist/debug.js +5 -5
- package/dist/debug.js.map +1 -1
- package/dist/definitions.d.ts +10 -6
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +52 -45
- package/dist/definitions.js.map +1 -1
- package/dist/extractSubgraphsFromSupergraph.js +20 -5
- package/dist/extractSubgraphsFromSupergraph.js.map +1 -1
- package/dist/federation.d.ts +3 -5
- package/dist/federation.d.ts.map +1 -1
- package/dist/federation.js +31 -24
- package/dist/federation.js.map +1 -1
- package/dist/inaccessibleSpec.d.ts +1 -1
- package/dist/inaccessibleSpec.d.ts.map +1 -1
- package/dist/inaccessibleSpec.js +5 -5
- package/dist/inaccessibleSpec.js.map +1 -1
- package/dist/joinSpec.d.ts.map +1 -1
- package/dist/joinSpec.js.map +1 -1
- package/dist/operations.js +1 -1
- package/dist/operations.js.map +1 -1
- package/dist/print.d.ts +1 -1
- package/dist/print.d.ts.map +1 -1
- package/dist/print.js +4 -4
- package/dist/print.js.map +1 -1
- package/dist/suggestions.js +1 -1
- package/dist/suggestions.js.map +1 -1
- package/dist/tagSpec.js.map +1 -1
- package/dist/utils.d.ts +15 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +64 -1
- package/dist/utils.js.map +1 -1
- package/dist/validate.js.map +1 -1
- package/dist/values.js +2 -3
- package/dist/values.js.map +1 -1
- package/package.json +5 -6
- package/src/__tests__/definitions.test.ts +3 -3
- package/src/__tests__/matchers/toMatchString.ts +2 -2
- package/src/__tests__/removeInaccessibleElements.test.ts +8 -8
- package/src/__tests__/utils.test.ts +92 -0
- package/src/buildSchema.ts +5 -5
- package/src/coreSpec.ts +12 -10
- package/src/debug.ts +6 -6
- package/src/definitions.ts +98 -75
- package/src/extractSubgraphsFromSupergraph.ts +31 -15
- package/src/federation.ts +56 -50
- package/src/inaccessibleSpec.ts +7 -7
- package/src/joinSpec.ts +0 -3
- package/src/operations.ts +20 -20
- package/src/print.ts +8 -8
- package/src/suggestions.ts +1 -1
- package/src/tagSpec.ts +1 -1
- package/src/types.ts +1 -1
- package/src/utils.ts +82 -0
- package/src/validate.ts +4 -4
- package/src/values.ts +4 -4
- package/tsconfig.test.tsbuildinfo +1 -1
- package/tsconfig.tsbuildinfo +1 -1
package/src/definitions.ts
CHANGED
|
@@ -135,23 +135,23 @@ export function isCustomScalarType(type: Type): boolean {
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
export function isIntType(type: Type): boolean {
|
|
138
|
-
return type === type.schema()
|
|
138
|
+
return type === type.schema().intType();
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
export function isStringType(type: Type): boolean {
|
|
142
|
-
return type === type.schema()
|
|
142
|
+
return type === type.schema().stringType();
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
export function isFloatType(type: Type): boolean {
|
|
146
|
-
return type === type.schema()
|
|
146
|
+
return type === type.schema().floatType();
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
export function isBooleanType(type: Type): boolean {
|
|
150
|
-
return type === type.schema()
|
|
150
|
+
return type === type.schema().booleanType();
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
export function isIDType(type: Type): boolean {
|
|
154
|
-
return type === type.schema()
|
|
154
|
+
return type === type.schema().idType();
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
export function isObjectType(type: Type): type is ObjectType {
|
|
@@ -323,7 +323,7 @@ export class DirectiveTargetElement<T extends DirectiveTargetElement<T>> {
|
|
|
323
323
|
): Directive<T, TApplicationArgs> {
|
|
324
324
|
let toAdd: Directive<T, TApplicationArgs>;
|
|
325
325
|
if (defOrDirective instanceof Directive) {
|
|
326
|
-
if (defOrDirective.schema()
|
|
326
|
+
if (defOrDirective.schema() != this.schema()) {
|
|
327
327
|
throw new Error(`Cannot add directive ${defOrDirective} to ${this} as it is attached to another schema`);
|
|
328
328
|
}
|
|
329
329
|
toAdd = defOrDirective;
|
|
@@ -377,24 +377,41 @@ abstract class Element<TParent extends SchemaElement<any, any> | Schema | Direct
|
|
|
377
377
|
protected _parent?: TParent;
|
|
378
378
|
sourceAST?: ASTNode;
|
|
379
379
|
|
|
380
|
-
schema(): Schema
|
|
380
|
+
schema(): Schema {
|
|
381
|
+
const schema = this.schemaInternal();
|
|
382
|
+
assert(schema, 'requested schema does not exist. Probably because the element is unattached');
|
|
383
|
+
return schema;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// this function exists because sometimes we can have an element that will be attached soon even though the current state is unattached
|
|
387
|
+
// (mainly for callbacks). Sometimes these intermediate states need to get the schema if it exists, but it may not.
|
|
388
|
+
// all external clients should use schema()
|
|
389
|
+
protected schemaInternal(): Schema | undefined {
|
|
381
390
|
if (!this._parent) {
|
|
382
391
|
return undefined;
|
|
383
392
|
} else if (this._parent instanceof Schema) {
|
|
384
|
-
// Note: at the time of this writing, it seems like typescript type-checking breaks a bit around generics.
|
|
393
|
+
// Note: at the time of this writing, it seems like typescript type-checking breaks a bit around generics.
|
|
385
394
|
// At this point of the code, `this._parent` is typed as 'TParent & Schema', but for some reason this is
|
|
386
395
|
// "not assignable to type 'Schema | undefined'" (which sounds wrong: if my type theory is not too broken,
|
|
387
396
|
// 'A & B' should always be assignable to both 'A' and 'B').
|
|
388
397
|
return this._parent as any;
|
|
389
|
-
} else {
|
|
390
|
-
return
|
|
398
|
+
} else if (this._parent instanceof SchemaElement) {
|
|
399
|
+
return this._parent.schemaInternal();
|
|
400
|
+
} else if (this._parent instanceof DirectiveTargetElement) {
|
|
401
|
+
return this._parent.schema();
|
|
391
402
|
}
|
|
403
|
+
assert(false, 'unreachable code. parent is of unknown type');
|
|
392
404
|
}
|
|
393
405
|
|
|
394
|
-
get parent(): TParent
|
|
406
|
+
get parent(): TParent {
|
|
407
|
+
assert(this._parent, 'trying to access non-existent parent');
|
|
395
408
|
return this._parent;
|
|
396
409
|
}
|
|
397
410
|
|
|
411
|
+
isAttached(): boolean {
|
|
412
|
+
return !!this._parent;
|
|
413
|
+
}
|
|
414
|
+
|
|
398
415
|
// Accessed only through Element.prototype['setParent'] (so we don't mark it protected as an override wouldn't be properly called).
|
|
399
416
|
private setParent(parent: TParent) {
|
|
400
417
|
assert(!this._parent, "Cannot set parent of an already attached element");
|
|
@@ -410,10 +427,10 @@ abstract class Element<TParent extends SchemaElement<any, any> | Schema | Direct
|
|
|
410
427
|
// Allowing to add element to a detached element would get hairy. Because that would mean that when you do attach an element,
|
|
411
428
|
// you have to recurse within that element to all children elements to check whether they are attached or not and to which
|
|
412
429
|
// schema. And if they aren't attached, attaching them as side-effect could be surprising (think that adding a single field
|
|
413
|
-
// to a schema could bring a whole
|
|
430
|
+
// to a schema could bring a whole hierarchy of types and directives for instance). If they are attached, it only work if
|
|
414
431
|
// it's to the same schema, but you have to check.
|
|
415
432
|
// Overall, it's simpler to force attaching elements before you add other elements to them.
|
|
416
|
-
if (!this.
|
|
433
|
+
if (!this.isAttached()) {
|
|
417
434
|
throw error(`Cannot modify detached element ${this}`);
|
|
418
435
|
}
|
|
419
436
|
}
|
|
@@ -449,8 +466,8 @@ export abstract class SchemaElement<TOwnType extends SchemaElement<any, TParent>
|
|
|
449
466
|
|
|
450
467
|
hasAppliedDirective(nameOrDefinition: string | DirectiveDefinition<any>): boolean {
|
|
451
468
|
// From the type-system point of view, there is no `appliedDirectivesOf(_: string | DirectiveDefinition)` function, but rather 2 overloads, neither of
|
|
452
|
-
// which can take 'string | DirectiveDefinition', hence the need for this
|
|
453
|
-
// on `
|
|
469
|
+
// which can take 'string | DirectiveDefinition', hence the need for this surprisingly looking code. And we don't really want to remove the overloading
|
|
470
|
+
// on `appliedDirectivesOf` because that would lose us the type-checking of arguments in the case where we pass a definition (or rather, we could
|
|
454
471
|
// preserve it, but it would make is a bit too easy to mess up calls with the 'string' argument).
|
|
455
472
|
return (typeof nameOrDefinition === 'string'
|
|
456
473
|
? this.appliedDirectivesOf(nameOrDefinition)
|
|
@@ -473,9 +490,9 @@ export abstract class SchemaElement<TOwnType extends SchemaElement<any, TParent>
|
|
|
473
490
|
let name: string;
|
|
474
491
|
if (typeof nameOrDefOrDirective === 'string') {
|
|
475
492
|
this.checkUpdate();
|
|
476
|
-
const def = this.schema()
|
|
493
|
+
const def = this.schema().directive(nameOrDefOrDirective);
|
|
477
494
|
if (!def) {
|
|
478
|
-
throw new GraphQLError(`Cannot apply
|
|
495
|
+
throw new GraphQLError(`Cannot apply unknown directive "@${nameOrDefOrDirective}"`);
|
|
479
496
|
}
|
|
480
497
|
name = nameOrDefOrDirective;
|
|
481
498
|
} else {
|
|
@@ -499,7 +516,7 @@ export abstract class SchemaElement<TOwnType extends SchemaElement<any, TParent>
|
|
|
499
516
|
}
|
|
500
517
|
|
|
501
518
|
protected onModification() {
|
|
502
|
-
const schema = this.
|
|
519
|
+
const schema = this.schemaInternal();
|
|
503
520
|
if (schema) {
|
|
504
521
|
Schema.prototype['onModification'].call(schema);
|
|
505
522
|
}
|
|
@@ -511,7 +528,7 @@ export abstract class SchemaElement<TOwnType extends SchemaElement<any, TParent>
|
|
|
511
528
|
|
|
512
529
|
protected removeTypeReferenceInternal(type: BaseNamedType<any, any>) {
|
|
513
530
|
// This method is a bit of a hack: we don't want to expose it and we call it from an other class, so we call it though
|
|
514
|
-
// `SchemaElement.prototype`, but we also want this to abstract as it can only be
|
|
531
|
+
// `SchemaElement.prototype`, but we also want this to abstract as it can only be implemented by each concrete subclass.
|
|
515
532
|
// As we can't have both at the same time, this method just delegate to `remoteTypeReference` which is genuinely
|
|
516
533
|
// abstract. This also allow to work around the typing issue that the type checker cannot tell that every BaseNamedType
|
|
517
534
|
// is a NamedType (because in theory, someone could extend BaseNamedType without listing it in NamedType; but as
|
|
@@ -522,16 +539,16 @@ export abstract class SchemaElement<TOwnType extends SchemaElement<any, TParent>
|
|
|
522
539
|
protected abstract removeTypeReference(type: NamedType): void;
|
|
523
540
|
|
|
524
541
|
protected checkRemoval() {
|
|
525
|
-
if (this.isElementBuiltIn() && !Schema.prototype['canModifyBuiltIn'].call(this.schema()
|
|
542
|
+
if (this.isElementBuiltIn() && !Schema.prototype['canModifyBuiltIn'].call(this.schema())) {
|
|
526
543
|
throw error(`Cannot modify built-in ${this}`);
|
|
527
544
|
}
|
|
528
545
|
// We allow removals even on detached element because that doesn't particularly create issues (and we happen to do such
|
|
529
546
|
// removals on detached internally; though of course we could refactor the code if we wanted).
|
|
530
547
|
}
|
|
531
548
|
|
|
532
|
-
protected checkUpdate(addedElement?: { schema(): Schema
|
|
549
|
+
protected checkUpdate(addedElement?: { schema(): Schema, isAttached(): boolean }) {
|
|
533
550
|
super.checkUpdate();
|
|
534
|
-
if (!Schema.prototype['canModifyBuiltIn'].call(this.schema()
|
|
551
|
+
if (!Schema.prototype['canModifyBuiltIn'].call(this.schema())) {
|
|
535
552
|
// Ensure this element (the modified one), is not a built-in, or part of one.
|
|
536
553
|
let thisElement: SchemaElement<TOwnType, any> | Schema | undefined = this;
|
|
537
554
|
while (thisElement && thisElement instanceof SchemaElement) {
|
|
@@ -541,7 +558,7 @@ export abstract class SchemaElement<TOwnType extends SchemaElement<any, TParent>
|
|
|
541
558
|
thisElement = thisElement.parent;
|
|
542
559
|
}
|
|
543
560
|
}
|
|
544
|
-
if (addedElement) {
|
|
561
|
+
if (addedElement && addedElement.isAttached()) {
|
|
545
562
|
const thatSchema = addedElement.schema();
|
|
546
563
|
if (thatSchema && thatSchema != this.schema()) {
|
|
547
564
|
throw error(`Cannot add element ${addedElement} to ${this} as it is attached to another schema`);
|
|
@@ -593,7 +610,7 @@ abstract class BaseNamedType<TReferencer, TOwnType extends NamedType & NamedSche
|
|
|
593
610
|
}
|
|
594
611
|
|
|
595
612
|
*allChildElements(): Generator<NamedSchemaElement<any, TOwnType, any>, void, undefined> {
|
|
596
|
-
// Overriden by those types that do have
|
|
613
|
+
// Overriden by those types that do have children
|
|
597
614
|
}
|
|
598
615
|
|
|
599
616
|
extensions(): ReadonlySet<Extension<TOwnType>> {
|
|
@@ -659,7 +676,7 @@ abstract class BaseNamedType<TReferencer, TOwnType extends NamedType & NamedSche
|
|
|
659
676
|
* reference.
|
|
660
677
|
*
|
|
661
678
|
* @returns an array of all the elements in the schema of this type (before the removal) that were
|
|
662
|
-
*
|
|
679
|
+
* referencing this type (and have thus now an undefined reference).
|
|
663
680
|
*/
|
|
664
681
|
remove(): TReferencer[] {
|
|
665
682
|
if (!this._parent) {
|
|
@@ -739,7 +756,7 @@ abstract class BaseExtensionMember<TExtended extends ExtendableElement> extends
|
|
|
739
756
|
setOfExtension(extension: Extension<TExtended> | undefined) {
|
|
740
757
|
this.checkUpdate();
|
|
741
758
|
// See similar comment on FieldDefinition.setOfExtension for why we have to cast.
|
|
742
|
-
if (extension && !this.
|
|
759
|
+
if (extension && !this._parent?.extensions().has(extension as any)) {
|
|
743
760
|
throw error(`Cannot set object as part of the provided extension: it is not an extension of parent ${this.parent}`);
|
|
744
761
|
}
|
|
745
762
|
this._extension = extension;
|
|
@@ -800,7 +817,7 @@ export class BuiltIns {
|
|
|
800
817
|
|
|
801
818
|
onValidation(schema: Schema, unvalidatedDirectives?: string[]): GraphQLError[] {
|
|
802
819
|
const errors: GraphQLError[] = [];
|
|
803
|
-
// We make sure that if any of the built-ins has been redefined, then the
|
|
820
|
+
// We make sure that if any of the built-ins has been redefined, then the redefinition is
|
|
804
821
|
// the same as the built-in one.
|
|
805
822
|
for (const type of schema.builtInTypes(undefined, true)) {
|
|
806
823
|
const maybeRedefined = schema.type(type.name)!;
|
|
@@ -1136,9 +1153,17 @@ export class Schema {
|
|
|
1136
1153
|
return this.apiSchema;
|
|
1137
1154
|
}
|
|
1138
1155
|
|
|
1139
|
-
toGraphQLJSSchema(): GraphQLSchema {
|
|
1156
|
+
toGraphQLJSSchema(isSubgraph: boolean = false): GraphQLSchema {
|
|
1140
1157
|
// Obviously not super fast, but as long as we don't use this often on a hot path, that's probably ok.
|
|
1141
|
-
|
|
1158
|
+
if (!isSubgraph) {
|
|
1159
|
+
return buildGraphqlSchemaFromAST(this.toAST());
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
// Some subgraphs, especially federation 1 ones, cannot be properly converted to a GraphQLSchema because they are invalid graphQL.
|
|
1163
|
+
// And the main culprit is type extensions that don't have a corresponding definition. So to avoid that problem, we print
|
|
1164
|
+
// up the AST without extensions. Another issue is the non graph built-ins which needs to be explicitely defined.
|
|
1165
|
+
const ast = parse(printSchema(this, { ...toASTPrintOptions, mergeTypesAndExtensions: true }), { noLocation: true });
|
|
1166
|
+
return buildGraphqlSchemaFromAST(ast);
|
|
1142
1167
|
}
|
|
1143
1168
|
|
|
1144
1169
|
get schemaDefinition(): SchemaDefinition {
|
|
@@ -1217,7 +1242,7 @@ export class Schema {
|
|
|
1217
1242
|
if (existing && !existing.isBuiltIn) {
|
|
1218
1243
|
throw error(`Type ${type} already exists in this schema`);
|
|
1219
1244
|
}
|
|
1220
|
-
if (type.
|
|
1245
|
+
if (type.isAttached()) {
|
|
1221
1246
|
// For convenience, let's not error out on adding an already added type.
|
|
1222
1247
|
if (type.parent == this) {
|
|
1223
1248
|
return type;
|
|
@@ -1302,7 +1327,7 @@ export class Schema {
|
|
|
1302
1327
|
if (existing && !existing.isBuiltIn) {
|
|
1303
1328
|
throw error(`Directive ${definition} already exists in this schema`);
|
|
1304
1329
|
}
|
|
1305
|
-
if (definition.
|
|
1330
|
+
if (definition.isAttached()) {
|
|
1306
1331
|
// For convenience, let's not error out on adding an already added directive.
|
|
1307
1332
|
if (definition.parent == this) {
|
|
1308
1333
|
return definition;
|
|
@@ -1398,7 +1423,7 @@ export class SchemaDefinition extends SchemaElement<SchemaDefinition, Schema> {
|
|
|
1398
1423
|
args?: TApplicationArgs
|
|
1399
1424
|
): Directive<SchemaDefinition, TApplicationArgs> {
|
|
1400
1425
|
const applied = super.applyDirective(nameOrDefOrDirective, args) as Directive<SchemaDefinition, TApplicationArgs>;
|
|
1401
|
-
const schema = this.schema()
|
|
1426
|
+
const schema = this.schema();
|
|
1402
1427
|
const coreFeatures = schema.coreFeatures;
|
|
1403
1428
|
if (isCoreSpecDirectiveApplication(applied)) {
|
|
1404
1429
|
if (coreFeatures) {
|
|
@@ -1428,7 +1453,7 @@ export class SchemaDefinition extends SchemaElement<SchemaDefinition, Schema> {
|
|
|
1428
1453
|
let toSet: RootType;
|
|
1429
1454
|
if (typeof nameOrType === 'string') {
|
|
1430
1455
|
this.checkUpdate();
|
|
1431
|
-
const obj = this.schema()
|
|
1456
|
+
const obj = this.schema().type(nameOrType);
|
|
1432
1457
|
if (!obj) {
|
|
1433
1458
|
throw new GraphQLError(`Cannot set schema ${rootKind} root to unknown type ${nameOrType}`);
|
|
1434
1459
|
} else if (obj.kind != 'ObjectType') {
|
|
@@ -1542,8 +1567,8 @@ abstract class FieldBasedType<T extends (ObjectType | InterfaceType) & NamedSche
|
|
|
1542
1567
|
// Note that we can only add the __typename built-in field when we're attached, because we need access to the
|
|
1543
1568
|
// schema string type. Also, we're effectively modifying a built-in (to add the type), so we
|
|
1544
1569
|
// need to let the schema accept it.
|
|
1545
|
-
Schema.prototype['runWithBuiltInModificationAllowed'].call(this.schema()
|
|
1546
|
-
this.addField(new FieldDefinition(typenameFieldName, true), new NonNullType(this.schema()
|
|
1570
|
+
Schema.prototype['runWithBuiltInModificationAllowed'].call(this.schema(), () => {
|
|
1571
|
+
this.addField(new FieldDefinition(typenameFieldName, true), new NonNullType(this.schema().stringType()));
|
|
1547
1572
|
});
|
|
1548
1573
|
}
|
|
1549
1574
|
|
|
@@ -1577,9 +1602,9 @@ abstract class FieldBasedType<T extends (ObjectType | InterfaceType) & NamedSche
|
|
|
1577
1602
|
let itf: InterfaceType;
|
|
1578
1603
|
if (typeof nameOrItfOrItfImpl === 'string') {
|
|
1579
1604
|
this.checkUpdate();
|
|
1580
|
-
const maybeItf = this.schema()
|
|
1605
|
+
const maybeItf = this.schema().type(nameOrItfOrItfImpl);
|
|
1581
1606
|
if (!maybeItf) {
|
|
1582
|
-
throw new GraphQLError(`Cannot implement
|
|
1607
|
+
throw new GraphQLError(`Cannot implement unknown type ${nameOrItfOrItfImpl}`);
|
|
1583
1608
|
} else if (maybeItf.kind != 'InterfaceType') {
|
|
1584
1609
|
throw new GraphQLError(`Cannot implement non-interface type ${nameOrItfOrItfImpl} (of type ${maybeItf.kind})`);
|
|
1585
1610
|
}
|
|
@@ -1718,9 +1743,6 @@ export class ObjectType extends FieldBasedType<ObjectType, ObjectTypeReferencer>
|
|
|
1718
1743
|
*/
|
|
1719
1744
|
isRootType(): boolean {
|
|
1720
1745
|
const schema = this.schema();
|
|
1721
|
-
if (!schema) {
|
|
1722
|
-
return false;
|
|
1723
|
-
}
|
|
1724
1746
|
return schema.schemaDefinition.roots().some(rt => rt.type == this);
|
|
1725
1747
|
}
|
|
1726
1748
|
|
|
@@ -1729,9 +1751,6 @@ export class ObjectType extends FieldBasedType<ObjectType, ObjectTypeReferencer>
|
|
|
1729
1751
|
*/
|
|
1730
1752
|
isQueryRootType(): boolean {
|
|
1731
1753
|
const schema = this.schema();
|
|
1732
|
-
if (!schema) {
|
|
1733
|
-
return false;
|
|
1734
|
-
}
|
|
1735
1754
|
return schema.schemaDefinition.root('query')?.type === this;
|
|
1736
1755
|
}
|
|
1737
1756
|
}
|
|
@@ -1773,10 +1792,10 @@ export class UnionType extends BaseNamedType<OutputTypeReferencer, UnionType> {
|
|
|
1773
1792
|
// Note that we can only create the __typename built-in field when we're attached, because we need access to the
|
|
1774
1793
|
// schema string type. Also, we're effectively modifying a built-in (to add the type), so we
|
|
1775
1794
|
// need to let the schema accept it.
|
|
1776
|
-
Schema.prototype['runWithBuiltInModificationAllowed'].call(this.schema()
|
|
1795
|
+
Schema.prototype['runWithBuiltInModificationAllowed'].call(this.schema(), () => {
|
|
1777
1796
|
this._typenameField = new FieldDefinition(typenameFieldName, true);
|
|
1778
1797
|
Element.prototype['setParent'].call(this._typenameField, this);
|
|
1779
|
-
this._typenameField.type = new NonNullType(this.schema()
|
|
1798
|
+
this._typenameField.type = new NonNullType(this.schema().stringType());
|
|
1780
1799
|
});
|
|
1781
1800
|
}
|
|
1782
1801
|
|
|
@@ -1805,9 +1824,9 @@ export class UnionType extends BaseNamedType<OutputTypeReferencer, UnionType> {
|
|
|
1805
1824
|
let obj: ObjectType;
|
|
1806
1825
|
if (typeof nameOrTypeOrMember === 'string') {
|
|
1807
1826
|
this.checkUpdate();
|
|
1808
|
-
const maybeObj = this.schema()
|
|
1827
|
+
const maybeObj = this.schema().type(nameOrTypeOrMember);
|
|
1809
1828
|
if (!maybeObj) {
|
|
1810
|
-
throw new GraphQLError(`Cannot add
|
|
1829
|
+
throw new GraphQLError(`Cannot add unknown type ${nameOrTypeOrMember} as member of union type ${this.name}`);
|
|
1811
1830
|
} else if (maybeObj.kind != 'ObjectType') {
|
|
1812
1831
|
throw new GraphQLError(`Cannot add non-object type ${nameOrTypeOrMember} (of type ${maybeObj.kind}) as member of union type ${this.name}`);
|
|
1813
1832
|
}
|
|
@@ -1839,7 +1858,7 @@ export class UnionType extends BaseNamedType<OutputTypeReferencer, UnionType> {
|
|
|
1839
1858
|
|
|
1840
1859
|
/**
|
|
1841
1860
|
* Access a field of the union by name.
|
|
1842
|
-
* As the only field that can be
|
|
1861
|
+
* As the only field that can be accessed on an union is the __typename one, this method will always return undefined unless called
|
|
1843
1862
|
* on "__typename". However, this exists to allow code working on CompositeType to be more generic.
|
|
1844
1863
|
*/
|
|
1845
1864
|
field(name: string): FieldDefinition<UnionType> | undefined {
|
|
@@ -1960,7 +1979,7 @@ export class InputObjectType extends BaseNamedType<InputTypeReferencer, InputObj
|
|
|
1960
1979
|
throw error(`Field ${toAdd.name} already exists on ${this}`);
|
|
1961
1980
|
}
|
|
1962
1981
|
if (type && !isInputType(type)) {
|
|
1963
|
-
throw error(`Invalid
|
|
1982
|
+
throw error(`Invalid output type ${type} for field ${toAdd.name}: input field types should be input types.`);
|
|
1964
1983
|
}
|
|
1965
1984
|
this._fields.set(toAdd.name, toAdd);
|
|
1966
1985
|
this._cachedFieldsArray = undefined;
|
|
@@ -2007,10 +2026,14 @@ class BaseWrapperType<T extends Type> {
|
|
|
2007
2026
|
assert(this._type, 'Cannot wrap an undefined/null type');
|
|
2008
2027
|
}
|
|
2009
2028
|
|
|
2010
|
-
schema(): Schema
|
|
2029
|
+
schema(): Schema {
|
|
2011
2030
|
return this.baseType().schema();
|
|
2012
2031
|
}
|
|
2013
2032
|
|
|
2033
|
+
isAttached(): boolean {
|
|
2034
|
+
return this.baseType().isAttached();
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2014
2037
|
get ofType(): T {
|
|
2015
2038
|
return this._type;
|
|
2016
2039
|
}
|
|
@@ -2058,7 +2081,7 @@ export class FieldDefinition<TParent extends CompositeType> extends NamedSchemaE
|
|
|
2058
2081
|
}
|
|
2059
2082
|
|
|
2060
2083
|
get coordinate(): string {
|
|
2061
|
-
const parent = this.
|
|
2084
|
+
const parent = this._parent;
|
|
2062
2085
|
return `${parent == undefined ? '<detached>' : parent.coordinate}.${this.name}`;
|
|
2063
2086
|
}
|
|
2064
2087
|
|
|
@@ -2099,7 +2122,7 @@ export class FieldDefinition<TParent extends CompositeType> extends NamedSchemaE
|
|
|
2099
2122
|
return existing;
|
|
2100
2123
|
}
|
|
2101
2124
|
if (type && !isInputType(type)) {
|
|
2102
|
-
throw error(`Invalid
|
|
2125
|
+
throw error(`Invalid output type ${type} for argument ${toAdd.name} of ${this}: arguments should be input types.`);
|
|
2103
2126
|
}
|
|
2104
2127
|
this._args.set(toAdd.name, toAdd);
|
|
2105
2128
|
Element.prototype['setParent'].call(toAdd, this);
|
|
@@ -2116,9 +2139,9 @@ export class FieldDefinition<TParent extends CompositeType> extends NamedSchemaE
|
|
|
2116
2139
|
|
|
2117
2140
|
setOfExtension(extension: Extension<TParent> | undefined) {
|
|
2118
2141
|
this.checkUpdate();
|
|
2119
|
-
// It seems
|
|
2142
|
+
// It seems typescript "expand" `TParent` below into `ObjectType | Interface`, so it essentially lose the context that
|
|
2120
2143
|
// the `TParent` in `Extension<TParent>` will always match. Hence the `as any`.
|
|
2121
|
-
if (extension && !this.
|
|
2144
|
+
if (extension && !this._parent?.extensions().has(extension as any)) {
|
|
2122
2145
|
throw error(`Cannot mark field ${this.name} as part of the provided extension: it is not an extension of field parent type ${this.parent}`);
|
|
2123
2146
|
}
|
|
2124
2147
|
this._extension = extension;
|
|
@@ -2171,7 +2194,7 @@ export class FieldDefinition<TParent extends CompositeType> extends NamedSchemaE
|
|
|
2171
2194
|
|
|
2172
2195
|
toString(): string {
|
|
2173
2196
|
const args = this._args.size == 0
|
|
2174
|
-
? ""
|
|
2197
|
+
? ""
|
|
2175
2198
|
: '(' + this.arguments().map(arg => arg.toString()).join(', ') + ')';
|
|
2176
2199
|
return `${this.name}${args}: ${this.type}`;
|
|
2177
2200
|
}
|
|
@@ -2183,7 +2206,7 @@ export class InputFieldDefinition extends NamedSchemaElementWithType<InputType,
|
|
|
2183
2206
|
defaultValue?: any
|
|
2184
2207
|
|
|
2185
2208
|
get coordinate(): string {
|
|
2186
|
-
const parent = this.
|
|
2209
|
+
const parent = this._parent;
|
|
2187
2210
|
return `${parent == undefined ? '<detached>' : parent.coordinate}.${this.name}`;
|
|
2188
2211
|
}
|
|
2189
2212
|
|
|
@@ -2197,9 +2220,9 @@ export class InputFieldDefinition extends NamedSchemaElementWithType<InputType,
|
|
|
2197
2220
|
|
|
2198
2221
|
setOfExtension(extension: Extension<InputObjectType> | undefined) {
|
|
2199
2222
|
this.checkUpdate();
|
|
2200
|
-
// It seems
|
|
2223
|
+
// It seems typescript "expand" `TParent` below into `ObjectType | Interface`, so it essentially lose the context that
|
|
2201
2224
|
// the `TParent` in `Extension<TParent>` will always match. Hence the `as any`.
|
|
2202
|
-
if (extension && !this.
|
|
2225
|
+
if (extension && !this._parent?.extensions().has(extension as any)) {
|
|
2203
2226
|
throw error(`Cannot mark field ${this.name} as part of the provided extension: it is not an extension of field parent type ${this.parent}`);
|
|
2204
2227
|
}
|
|
2205
2228
|
this._extension = extension;
|
|
@@ -2243,7 +2266,7 @@ export class ArgumentDefinition<TParent extends FieldDefinition<any> | Directive
|
|
|
2243
2266
|
}
|
|
2244
2267
|
|
|
2245
2268
|
get coordinate(): string {
|
|
2246
|
-
const parent = this.
|
|
2269
|
+
const parent = this._parent;
|
|
2247
2270
|
return `${parent == undefined ? '<detached>' : parent.coordinate}(${this.name}:)`;
|
|
2248
2271
|
}
|
|
2249
2272
|
|
|
@@ -2288,7 +2311,7 @@ export class EnumValue extends NamedSchemaElement<EnumValue, EnumType, never> {
|
|
|
2288
2311
|
private _extension?: Extension<EnumType>;
|
|
2289
2312
|
|
|
2290
2313
|
get coordinate(): string {
|
|
2291
|
-
const parent = this.
|
|
2314
|
+
const parent = this._parent;
|
|
2292
2315
|
return `${parent == undefined ? '<detached>' : parent.coordinate}.${this.name}`;
|
|
2293
2316
|
}
|
|
2294
2317
|
|
|
@@ -2298,7 +2321,7 @@ export class EnumValue extends NamedSchemaElement<EnumValue, EnumType, never> {
|
|
|
2298
2321
|
|
|
2299
2322
|
setOfExtension(extension: Extension<EnumType> | undefined) {
|
|
2300
2323
|
this.checkUpdate();
|
|
2301
|
-
if (extension && !this.
|
|
2324
|
+
if (extension && !this._parent?.extensions().has(extension)) {
|
|
2302
2325
|
throw error(`Cannot mark field ${this.name} as part of the provided extension: it is not an extension of field parent type ${this.parent}`);
|
|
2303
2326
|
}
|
|
2304
2327
|
this._extension = extension;
|
|
@@ -2485,13 +2508,13 @@ export class Directive<
|
|
|
2485
2508
|
super();
|
|
2486
2509
|
}
|
|
2487
2510
|
|
|
2488
|
-
schema(): Schema
|
|
2489
|
-
return this.
|
|
2511
|
+
schema(): Schema {
|
|
2512
|
+
return this.parent.schema();
|
|
2490
2513
|
}
|
|
2491
2514
|
|
|
2492
2515
|
get definition(): DirectiveDefinition | undefined {
|
|
2493
2516
|
const doc = this.schema();
|
|
2494
|
-
return doc
|
|
2517
|
+
return doc.directive(this.name);
|
|
2495
2518
|
}
|
|
2496
2519
|
|
|
2497
2520
|
arguments(includeDefaultValues: boolean = false) : Readonly<TArgs> {
|
|
@@ -2516,7 +2539,7 @@ export class Directive<
|
|
|
2516
2539
|
}
|
|
2517
2540
|
|
|
2518
2541
|
private isAttachedToSchemaElement(): boolean {
|
|
2519
|
-
return this.
|
|
2542
|
+
return this.isAttached();
|
|
2520
2543
|
}
|
|
2521
2544
|
|
|
2522
2545
|
setArguments(args: TArgs) {
|
|
@@ -2533,7 +2556,7 @@ export class Directive<
|
|
|
2533
2556
|
if (entries.length !== Object.keys(expectedArgs).length) {
|
|
2534
2557
|
return false;
|
|
2535
2558
|
}
|
|
2536
|
-
for (
|
|
2559
|
+
for (const [key, val] of entries) {
|
|
2537
2560
|
if (!(key in expectedArgs)) {
|
|
2538
2561
|
return false;
|
|
2539
2562
|
}
|
|
@@ -2552,7 +2575,7 @@ export class Directive<
|
|
|
2552
2575
|
setOfExtension(extension: Extension<any> | undefined) {
|
|
2553
2576
|
this.checkUpdate();
|
|
2554
2577
|
if (extension) {
|
|
2555
|
-
const parent = this.parent
|
|
2578
|
+
const parent = this.parent;
|
|
2556
2579
|
if (parent instanceof SchemaDefinition || parent instanceof BaseNamedType) {
|
|
2557
2580
|
if (!parent.extensions().has(extension)) {
|
|
2558
2581
|
throw error(`Cannot mark directive ${this.name} as part of the provided extension: it is not an extension of parent ${parent}`);
|
|
@@ -2592,15 +2615,15 @@ export class Directive<
|
|
|
2592
2615
|
return false;
|
|
2593
2616
|
}
|
|
2594
2617
|
this.onModification();
|
|
2595
|
-
const coreFeatures = this.schema()
|
|
2618
|
+
const coreFeatures = this.schema().coreFeatures;
|
|
2596
2619
|
if (coreFeatures && this.name === coreFeatures.coreItself.nameInSchema) {
|
|
2597
2620
|
// We're removing a @core directive application, so we remove it from the list of core features. And
|
|
2598
2621
|
// if it is @core itself, we clean all features (to avoid having things too inconsistent).
|
|
2599
2622
|
const url = FeatureUrl.parse(this._args['feature']!);
|
|
2600
2623
|
if (url.identity === coreFeatures.coreItself.url.identity) {
|
|
2601
2624
|
// Note that we unmark first because the loop after that will nuke our parent.
|
|
2602
|
-
Schema.prototype['unmarkAsCoreSchema'].call(this.schema()
|
|
2603
|
-
for (const d of this.schema()
|
|
2625
|
+
Schema.prototype['unmarkAsCoreSchema'].call(this.schema());
|
|
2626
|
+
for (const d of this.schema().schemaDefinition.appliedDirectivesOf(coreFeatures.coreItself.nameInSchema)) {
|
|
2604
2627
|
d.removeInternal();
|
|
2605
2628
|
}
|
|
2606
2629
|
// The loop above will already have call removeInternal on this instance, so we can return
|
|
@@ -2971,10 +2994,10 @@ function copyAppliedDirectives(source: SchemaElement<any, any>, dest: SchemaElem
|
|
|
2971
2994
|
}
|
|
2972
2995
|
|
|
2973
2996
|
function copyFieldDefinitionInner<P extends ObjectType | InterfaceType>(source: FieldDefinition<P>, dest: FieldDefinition<P>) {
|
|
2974
|
-
const type = copyWrapperTypeOrTypeRef(source.type, dest.schema()
|
|
2997
|
+
const type = copyWrapperTypeOrTypeRef(source.type, dest.schema()) as OutputType;
|
|
2975
2998
|
dest.type = type;
|
|
2976
2999
|
for (const arg of source.arguments()) {
|
|
2977
|
-
const argType = copyWrapperTypeOrTypeRef(arg.type, dest.schema()
|
|
3000
|
+
const argType = copyWrapperTypeOrTypeRef(arg.type, dest.schema());
|
|
2978
3001
|
copyArgumentDefinitionInner(arg, dest.addArgument(arg.name, argType as InputType));
|
|
2979
3002
|
}
|
|
2980
3003
|
copyAppliedDirectives(source, dest);
|
|
@@ -2983,7 +3006,7 @@ function copyFieldDefinitionInner<P extends ObjectType | InterfaceType>(source:
|
|
|
2983
3006
|
}
|
|
2984
3007
|
|
|
2985
3008
|
function copyInputFieldDefinitionInner(source: InputFieldDefinition, dest: InputFieldDefinition) {
|
|
2986
|
-
const type = copyWrapperTypeOrTypeRef(source.type, dest.schema()
|
|
3009
|
+
const type = copyWrapperTypeOrTypeRef(source.type, dest.schema()) as InputType;
|
|
2987
3010
|
dest.type = type;
|
|
2988
3011
|
dest.defaultValue = source.defaultValue;
|
|
2989
3012
|
copyAppliedDirectives(source, dest);
|
|
@@ -3006,7 +3029,7 @@ function copyWrapperTypeOrTypeRef(source: Type | undefined, destParent: Schema):
|
|
|
3006
3029
|
}
|
|
3007
3030
|
|
|
3008
3031
|
function copyArgumentDefinitionInner<P extends FieldDefinition<any> | DirectiveDefinition>(source: ArgumentDefinition<P>, dest: ArgumentDefinition<P>) {
|
|
3009
|
-
const type = copyWrapperTypeOrTypeRef(source.type, dest.schema()
|
|
3032
|
+
const type = copyWrapperTypeOrTypeRef(source.type, dest.schema()) as InputType;
|
|
3010
3033
|
dest.type = type;
|
|
3011
3034
|
dest.defaultValue = source.defaultValue;
|
|
3012
3035
|
copyAppliedDirectives(source, dest);
|
|
@@ -3016,7 +3039,7 @@ function copyArgumentDefinitionInner<P extends FieldDefinition<any> | DirectiveD
|
|
|
3016
3039
|
|
|
3017
3040
|
function copyDirectiveDefinitionInner(source: DirectiveDefinition, dest: DirectiveDefinition) {
|
|
3018
3041
|
for (const arg of source.arguments()) {
|
|
3019
|
-
const type = copyWrapperTypeOrTypeRef(arg.type, dest.schema()
|
|
3042
|
+
const type = copyWrapperTypeOrTypeRef(arg.type, dest.schema());
|
|
3020
3043
|
copyArgumentDefinitionInner(arg, dest.addArgument(arg.name, type as InputType));
|
|
3021
3044
|
}
|
|
3022
3045
|
dest.repeatable = source.repeatable;
|