@apollo/federation-internals 2.4.1 → 2.4.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 +14 -0
- package/dist/argumentCompositionStrategies.d.ts +34 -0
- package/dist/argumentCompositionStrategies.d.ts.map +1 -0
- package/dist/argumentCompositionStrategies.js +35 -0
- package/dist/argumentCompositionStrategies.js.map +1 -0
- package/dist/coreSpec.d.ts +12 -3
- package/dist/coreSpec.d.ts.map +1 -1
- package/dist/coreSpec.js +68 -17
- package/dist/coreSpec.js.map +1 -1
- package/dist/definitions.d.ts +1 -0
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +30 -27
- package/dist/definitions.js.map +1 -1
- package/dist/directiveAndTypeSpecification.d.ts +26 -7
- package/dist/directiveAndTypeSpecification.d.ts.map +1 -1
- package/dist/directiveAndTypeSpecification.js +56 -4
- package/dist/directiveAndTypeSpecification.js.map +1 -1
- package/dist/federation.d.ts.map +1 -1
- package/dist/federation.js +24 -2
- package/dist/federation.js.map +1 -1
- package/dist/federationSpec.d.ts +2 -13
- package/dist/federationSpec.d.ts.map +1 -1
- package/dist/federationSpec.js +10 -60
- package/dist/federationSpec.js.map +1 -1
- package/dist/inaccessibleSpec.d.ts +0 -2
- package/dist/inaccessibleSpec.d.ts.map +1 -1
- package/dist/inaccessibleSpec.js +3 -6
- package/dist/inaccessibleSpec.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/knownCoreFeatures.d.ts +1 -0
- package/dist/knownCoreFeatures.d.ts.map +1 -1
- package/dist/knownCoreFeatures.js +5 -1
- package/dist/knownCoreFeatures.js.map +1 -1
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +10 -2
- package/dist/operations.js.map +1 -1
- package/dist/print.d.ts +7 -1
- package/dist/print.d.ts.map +1 -1
- package/dist/print.js +33 -5
- package/dist/print.js.map +1 -1
- package/dist/tagSpec.d.ts +0 -2
- package/dist/tagSpec.d.ts.map +1 -1
- package/dist/tagSpec.js +4 -10
- package/dist/tagSpec.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/directiveAndTypeSpecifications.test.ts +41 -0
- package/src/argumentCompositionStrategies.ts +39 -0
- package/src/coreSpec.ts +94 -34
- package/src/definitions.ts +35 -29
- package/src/directiveAndTypeSpecification.ts +101 -14
- package/src/federation.ts +33 -4
- package/src/federationSpec.ts +13 -73
- package/src/inaccessibleSpec.ts +4 -11
- package/src/index.ts +3 -0
- package/src/knownCoreFeatures.ts +9 -0
- package/src/operations.ts +14 -4
- package/src/print.ts +39 -4
- package/src/tagSpec.ts +4 -12
- package/tsconfig.tsbuildinfo +1 -1
package/dist/tagSpec.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tagSpec.js","sourceRoot":"","sources":["../src/tagSpec.ts"],"names":[],"mappings":";;;AAAA,qCAA0D;AAC1D,yCAA+F;AAC/F,+CAAyE;AACzE,mFAAuG;AACvG,mCAAiC;AACjC,2DAA2D;AAC3D,mCAAmC;AAEtB,QAAA,WAAW,GAAG,8BAA8B,CAAC;AAE1D,MAAa,iBAAkB,SAAQ,4BAAiB;IAKtD,YAAY,OAAuB;QACjC,KAAK,CAAC,IAAI,qBAAU,CAAC,mBAAW,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;QACnD,IAAI,CAAC,YAAY,GAAG;YAClB,2BAAiB,CAAC,gBAAgB;YAClC,2BAAiB,CAAC,MAAM;YACxB,2BAAiB,CAAC,SAAS;YAC3B,2BAAiB,CAAC,KAAK;SACxB,CAAC;QACF,IAAI,CAAC,oBAAoB,GAAG,2FAA2F,CAAC;QACxH,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE;YACjB,IAAI,CAAC,YAAY,CAAC,IAAI,CACpB,2BAAiB,CAAC,mBAAmB,EACrC,2BAAiB,CAAC,MAAM,EACxB,2BAAiB,CAAC,IAAI,EACtB,2BAAiB,CAAC,UAAU,EAC5B,2BAAiB,CAAC,YAAY,EAC9B,2BAAiB,CAAC,sBAAsB,CACzC,CAAC;YACF,IAAI,CAAC,oBAAoB,GAAG,sLAAsL,CAAC;YACnN,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE;gBACjB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,2BAAiB,CAAC,MAAM,CAAC,CAAC;gBACjD,IAAI,CAAC,oBAAoB,GAAG,+LAA+L,CAAC;aAC7N;SACF;QACD,IAAI,CAAC,gBAAgB,GAAG,IAAA,4DAA4B,EAAC;YACnD,IAAI,EAAC,KAAK;YACV,SAAS,EAAE,IAAI,CAAC,YAAY;YAC5B,UAAU,EAAE,IAAI;YAChB,
|
|
1
|
+
{"version":3,"file":"tagSpec.js","sourceRoot":"","sources":["../src/tagSpec.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { DirectiveLocation } from "graphql";
|
|
2
|
+
import "../definitions";
|
|
3
|
+
import { createDirectiveSpecification } from "../directiveAndTypeSpecification";
|
|
4
|
+
import { ARGUMENT_COMPOSITION_STRATEGIES } from "../argumentCompositionStrategies";
|
|
5
|
+
import { TAG_VERSIONS } from "../tagSpec";
|
|
6
|
+
|
|
7
|
+
const supergraphSpecification = () => TAG_VERSIONS.latest();
|
|
8
|
+
|
|
9
|
+
test('must have supergraph link if composed', () => {
|
|
10
|
+
expect(() => createDirectiveSpecification({
|
|
11
|
+
name: 'foo',
|
|
12
|
+
locations: [DirectiveLocation.OBJECT],
|
|
13
|
+
composes: true,
|
|
14
|
+
})).toThrow('Should provide a @link specification to use in supergraph for directive @foo if it composes');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('must have a merge strategy on all arguments if any', () => {
|
|
18
|
+
expect(() => createDirectiveSpecification({
|
|
19
|
+
name: 'foo',
|
|
20
|
+
locations: [DirectiveLocation.OBJECT],
|
|
21
|
+
composes: true,
|
|
22
|
+
supergraphSpecification,
|
|
23
|
+
args: [
|
|
24
|
+
{ name: "v1", type: (schema) => schema.intType(), compositionStrategy: ARGUMENT_COMPOSITION_STRATEGIES.MAX },
|
|
25
|
+
{ name: "v2", type: (schema) => schema.intType() }
|
|
26
|
+
],
|
|
27
|
+
})).toThrow('Invalid directive specification for @foo: not all arguments define a composition strategy');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('must be not be repeatable if it has a merge strategy', () => {
|
|
31
|
+
expect(() => createDirectiveSpecification({
|
|
32
|
+
name: 'foo',
|
|
33
|
+
locations: [DirectiveLocation.OBJECT],
|
|
34
|
+
composes: true,
|
|
35
|
+
repeatable: true,
|
|
36
|
+
supergraphSpecification,
|
|
37
|
+
args: [
|
|
38
|
+
{ name: "v", type: (schema) => schema.intType(), compositionStrategy: ARGUMENT_COMPOSITION_STRATEGIES.MAX },
|
|
39
|
+
],
|
|
40
|
+
})).toThrow('Invalid directive specification for @foo: @foo is repeatable and should not define composition strategy for its arguments');
|
|
41
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { InputType, ListType, NonNullType, Schema } from "./definitions"
|
|
2
|
+
|
|
3
|
+
export type ArgumentCompositionStrategy = {
|
|
4
|
+
name: string,
|
|
5
|
+
supportedTypes: (schema: Schema) => InputType[],
|
|
6
|
+
mergeValues: (values: any[]) => any,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const ARGUMENT_COMPOSITION_STRATEGIES = {
|
|
10
|
+
MAX: {
|
|
11
|
+
name: 'MAX',
|
|
12
|
+
supportedTypes: (schema: Schema) => [new NonNullType(schema.intType())],
|
|
13
|
+
mergeValues: (values: any[]) => Math.max(...values),
|
|
14
|
+
},
|
|
15
|
+
MIN: {
|
|
16
|
+
name: 'MIN',
|
|
17
|
+
supportedTypes: (schema: Schema) => [new NonNullType(schema.intType())],
|
|
18
|
+
mergeValues: (values: any[]) => Math.min(...values),
|
|
19
|
+
},
|
|
20
|
+
SUM: {
|
|
21
|
+
name: 'SUM',
|
|
22
|
+
supportedTypes: (schema: Schema) => [new NonNullType(schema.intType())],
|
|
23
|
+
mergeValues: (values: any[]) => values.reduce((acc, val) => acc + val, 0),
|
|
24
|
+
},
|
|
25
|
+
INTERSECTION: {
|
|
26
|
+
name: 'INTERSECTION',
|
|
27
|
+
supportedTypes: (schema: Schema) => schema.builtInScalarTypes().map((t) => new NonNullType(new ListType(new NonNullType(t)))),
|
|
28
|
+
mergeValues: (values: any[]) => values.reduce((acc, val) => acc.filter((v: any) => val.includes(v)), values[0]),
|
|
29
|
+
},
|
|
30
|
+
UNION: {
|
|
31
|
+
name: 'UNION',
|
|
32
|
+
supportedTypes: (schema: Schema) => schema.builtInScalarTypes().map((t) => new NonNullType(new ListType(new NonNullType(t)))),
|
|
33
|
+
mergeValues: (values: any[]) =>
|
|
34
|
+
values.reduce((acc, val) => {
|
|
35
|
+
const newValues = val.filter((v: any) => !acc.includes(v));
|
|
36
|
+
return acc.concat(newValues);
|
|
37
|
+
}, []),
|
|
38
|
+
},
|
|
39
|
+
}
|
package/src/coreSpec.ts
CHANGED
|
@@ -2,12 +2,12 @@ import { ASTNode, DirectiveLocation, GraphQLError, StringValueNode } from "graph
|
|
|
2
2
|
import { URL } from "url";
|
|
3
3
|
import { CoreFeature, Directive, DirectiveDefinition, EnumType, ErrGraphQLAPISchemaValidationFailed, ErrGraphQLValidationFailed, InputType, ListType, NamedType, NonNullType, ScalarType, Schema, SchemaDefinition, SchemaElement, sourceASTs } from "./definitions";
|
|
4
4
|
import { sameType } from "./types";
|
|
5
|
-
import { assert, firstOf } from './utils';
|
|
5
|
+
import { assert, firstOf, MapWithCachedArrays } from './utils';
|
|
6
6
|
import { aggregateError, ERRORS } from "./error";
|
|
7
7
|
import { valueToString } from "./values";
|
|
8
8
|
import { coreFeatureDefinitionIfKnown, registerKnownFeature } from "./knownCoreFeatures";
|
|
9
9
|
import { didYouMean, suggestionList } from "./suggestions";
|
|
10
|
-
import { ArgumentSpecification, createDirectiveSpecification, createEnumTypeSpecification, createScalarTypeSpecification, DirectiveSpecification, TypeSpecification } from "./directiveAndTypeSpecification";
|
|
10
|
+
import { ArgumentSpecification, createDirectiveSpecification, createEnumTypeSpecification, createScalarTypeSpecification, DirectiveCompositionSpecification, DirectiveSpecification, TypeSpecification } from "./directiveAndTypeSpecification";
|
|
11
11
|
|
|
12
12
|
export const coreIdentity = 'https://specs.apollo.dev/core';
|
|
13
13
|
export const linkIdentity = 'https://specs.apollo.dev/link';
|
|
@@ -38,10 +38,37 @@ function purposesDescription(purpose: CorePurpose) {
|
|
|
38
38
|
export abstract class FeatureDefinition {
|
|
39
39
|
readonly url: FeatureUrl;
|
|
40
40
|
|
|
41
|
+
private readonly _directiveSpecs = new MapWithCachedArrays<string, DirectiveSpecification>();
|
|
42
|
+
private readonly _typeSpecs = new MapWithCachedArrays<string, TypeSpecification>();
|
|
43
|
+
|
|
41
44
|
constructor(url: FeatureUrl | string) {
|
|
42
45
|
this.url = typeof url === 'string' ? FeatureUrl.parse(url) : url;
|
|
43
46
|
}
|
|
44
47
|
|
|
48
|
+
protected registerDirective(spec: DirectiveSpecification) {
|
|
49
|
+
this._directiveSpecs.set(spec.name, spec);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
protected registerType(spec: TypeSpecification) {
|
|
53
|
+
this._typeSpecs.set(spec.name, spec);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
directiveSpecs(): readonly DirectiveSpecification[] {
|
|
57
|
+
return this._directiveSpecs.values();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
directiveSpec(name: string): DirectiveSpecification | undefined {
|
|
61
|
+
return this._directiveSpecs.get(name);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
typeSpecs(): readonly TypeSpecification[] {
|
|
65
|
+
return this._typeSpecs.values();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
typeSpec(name: string): TypeSpecification | undefined {
|
|
69
|
+
return this._typeSpecs.get(name);
|
|
70
|
+
}
|
|
71
|
+
|
|
45
72
|
get identity(): string {
|
|
46
73
|
return this.url.identity;
|
|
47
74
|
}
|
|
@@ -60,9 +87,25 @@ export abstract class FeatureDefinition {
|
|
|
60
87
|
return nameInSchema != undefined && (directive.name === nameInSchema || directive.name.startsWith(`${nameInSchema}__`));
|
|
61
88
|
}
|
|
62
89
|
|
|
63
|
-
|
|
90
|
+
addElementsToSchema(schema: Schema): GraphQLError[] {
|
|
91
|
+
const feature = this.featureInSchema(schema);
|
|
92
|
+
assert(feature, 'The federation specification should have been added to the schema before this is called');
|
|
64
93
|
|
|
65
|
-
|
|
94
|
+
let errors: GraphQLError[] = [];
|
|
95
|
+
for (const type of this.typeSpecs()) {
|
|
96
|
+
errors = errors.concat(this.addTypeSpec(schema, type));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
for (const directive of this.directiveSpecs()) {
|
|
100
|
+
errors = errors.concat(this.addDirectiveSpec(schema, directive));
|
|
101
|
+
}
|
|
102
|
+
return errors;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
allElementNames(): string[] {
|
|
106
|
+
return this.directiveSpecs().map((spec) => `@${spec.name}`)
|
|
107
|
+
.concat(this.typeSpecs().map((spec) => spec.name));
|
|
108
|
+
}
|
|
66
109
|
|
|
67
110
|
protected nameInSchema(schema: Schema): string | undefined {
|
|
68
111
|
const feature = this.featureInSchema(schema);
|
|
@@ -79,12 +122,12 @@ export abstract class FeatureDefinition {
|
|
|
79
122
|
return feature ? feature.typeNameInSchema(typeName) : undefined;
|
|
80
123
|
}
|
|
81
124
|
|
|
82
|
-
protected rootDirective<TApplicationArgs extends {[key: string]: any}>(schema: Schema): DirectiveDefinition<TApplicationArgs> | undefined {
|
|
125
|
+
protected rootDirective<TApplicationArgs extends { [key: string]: any }>(schema: Schema): DirectiveDefinition<TApplicationArgs> | undefined {
|
|
83
126
|
const name = this.nameInSchema(schema);
|
|
84
127
|
return name ? schema.directive(name) as DirectiveDefinition<TApplicationArgs> | undefined : undefined;
|
|
85
128
|
}
|
|
86
129
|
|
|
87
|
-
protected directive<TApplicationArgs extends {[key: string]: any}>(schema: Schema, elementName: string): DirectiveDefinition<TApplicationArgs> | undefined {
|
|
130
|
+
protected directive<TApplicationArgs extends { [key: string]: any }>(schema: Schema, elementName: string): DirectiveDefinition<TApplicationArgs> | undefined {
|
|
88
131
|
const name = this.directiveNameInSchema(schema, elementName);
|
|
89
132
|
return name ? schema.directive(name) as DirectiveDefinition<TApplicationArgs> | undefined : undefined;
|
|
90
133
|
}
|
|
@@ -130,11 +173,17 @@ export abstract class FeatureDefinition {
|
|
|
130
173
|
return undefined;
|
|
131
174
|
}
|
|
132
175
|
|
|
176
|
+
compositionSpecification(directiveNameInFeature: string): DirectiveCompositionSpecification | undefined {
|
|
177
|
+
const spec = this._directiveSpecs.get(directiveNameInFeature);
|
|
178
|
+
return spec?.composition;
|
|
179
|
+
}
|
|
180
|
+
|
|
133
181
|
toString(): string {
|
|
134
182
|
return `${this.identity}/${this.version}`
|
|
135
183
|
}
|
|
136
184
|
}
|
|
137
185
|
|
|
186
|
+
|
|
138
187
|
export type CoreDirectiveArgs = {
|
|
139
188
|
url: undefined,
|
|
140
189
|
feature: string,
|
|
@@ -289,7 +338,7 @@ export function isCoreSpecDirectiveApplication(directive: Directive<SchemaDefini
|
|
|
289
338
|
if (url.identity === coreIdentity) {
|
|
290
339
|
return directive.name === (args.as ?? 'core');
|
|
291
340
|
} else {
|
|
292
|
-
return url.identity === linkIdentity &&
|
|
341
|
+
return url.identity === linkIdentity && directive.name === (args.as ?? linkDirectiveDefaultName);
|
|
293
342
|
}
|
|
294
343
|
} catch (err) {
|
|
295
344
|
return false;
|
|
@@ -307,7 +356,7 @@ function isValidUrlArgumentType(type: InputType, schema: Schema): boolean {
|
|
|
307
356
|
|
|
308
357
|
const linkPurposeTypeSpec = createEnumTypeSpecification({
|
|
309
358
|
name: 'Purpose',
|
|
310
|
-
values: corePurposes.map((name) => ({ name, description: purposesDescription(name)}))
|
|
359
|
+
values: corePurposes.map((name) => ({ name, description: purposesDescription(name) }))
|
|
311
360
|
});
|
|
312
361
|
|
|
313
362
|
const linkImportTypeSpec = createScalarTypeSpecification({ name: 'Import' });
|
|
@@ -321,32 +370,43 @@ export class CoreSpecDefinition extends FeatureDefinition {
|
|
|
321
370
|
name,
|
|
322
371
|
locations: [DirectiveLocation.SCHEMA],
|
|
323
372
|
repeatable: true,
|
|
324
|
-
|
|
373
|
+
args: this.createDefinitionArgumentSpecifications(),
|
|
325
374
|
});
|
|
375
|
+
this.registerDirective(this.directiveDefinitionSpec);
|
|
326
376
|
}
|
|
327
377
|
|
|
328
|
-
private createDefinitionArgumentSpecifications(
|
|
378
|
+
private createDefinitionArgumentSpecifications(): ArgumentSpecification[] {
|
|
329
379
|
const args: ArgumentSpecification[] = [
|
|
330
|
-
{ name: this.urlArgName(), type: schema.stringType() },
|
|
331
|
-
{ name: 'as', type: schema.stringType() },
|
|
380
|
+
{ name: this.urlArgName(), type: (schema) => schema.stringType() },
|
|
381
|
+
{ name: 'as', type: (schema) => schema.stringType() },
|
|
332
382
|
];
|
|
333
383
|
if (this.supportPurposes()) {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
384
|
+
args.push({
|
|
385
|
+
name: 'for',
|
|
386
|
+
type: (schema, nameInSchema) => {
|
|
387
|
+
const purposeName = `${nameInSchema ?? this.url.name}__${linkPurposeTypeSpec.name}`;
|
|
388
|
+
const errors = linkPurposeTypeSpec.checkOrAdd(schema, purposeName);
|
|
389
|
+
if (errors.length > 0) {
|
|
390
|
+
return errors;
|
|
391
|
+
}
|
|
392
|
+
return schema.type(purposeName) as InputType;
|
|
393
|
+
},
|
|
394
|
+
});
|
|
340
395
|
}
|
|
341
396
|
if (this.supportImport()) {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
397
|
+
args.push({
|
|
398
|
+
name: 'import',
|
|
399
|
+
type: (schema, nameInSchema) => {
|
|
400
|
+
const importName = `${nameInSchema ?? this.url.name}__${linkImportTypeSpec.name}`;
|
|
401
|
+
const errors = linkImportTypeSpec.checkOrAdd(schema, importName);
|
|
402
|
+
if (errors.length > 0) {
|
|
403
|
+
return errors;
|
|
404
|
+
}
|
|
405
|
+
return new ListType(schema.type(importName)!);
|
|
406
|
+
}
|
|
407
|
+
});
|
|
348
408
|
}
|
|
349
|
-
return
|
|
409
|
+
return args;
|
|
350
410
|
}
|
|
351
411
|
|
|
352
412
|
addElementsToSchema(_: Schema): GraphQLError[] {
|
|
@@ -394,7 +454,7 @@ export class CoreSpecDefinition extends FeatureDefinition {
|
|
|
394
454
|
//
|
|
395
455
|
// So instead, we put the directive on the schema definition unless some extensions exists but no
|
|
396
456
|
// definition does (that is, no non-extension elements are populated).
|
|
397
|
-
const schemaDef =
|
|
457
|
+
const schemaDef = schema.schemaDefinition;
|
|
398
458
|
// Side-note: this test must be done _before_ we call `applyDirective`, otherwise it would take it into
|
|
399
459
|
// account.
|
|
400
460
|
const hasDefinition = schemaDef.hasNonExtensionElements();
|
|
@@ -429,7 +489,7 @@ export class CoreSpecDefinition extends FeatureDefinition {
|
|
|
429
489
|
* must start with a `@`.
|
|
430
490
|
*/
|
|
431
491
|
allElementNames(): string[] {
|
|
432
|
-
const names = [
|
|
492
|
+
const names = [`@${this.url.name}`];
|
|
433
493
|
if (this.supportPurposes()) {
|
|
434
494
|
names.push('Purpose');
|
|
435
495
|
}
|
|
@@ -533,7 +593,7 @@ export class FeatureDefinitions<T extends FeatureDefinition = FeatureDefinition>
|
|
|
533
593
|
* Versions are a (major, minor) number pair.
|
|
534
594
|
*/
|
|
535
595
|
export class FeatureVersion {
|
|
536
|
-
constructor(public readonly major: number, public readonly minor: number) {}
|
|
596
|
+
constructor(public readonly major: number, public readonly minor: number) { }
|
|
537
597
|
|
|
538
598
|
/**
|
|
539
599
|
* Parse a version specifier of the form "v(major).(minor)" or throw
|
|
@@ -565,8 +625,8 @@ export class FeatureVersion {
|
|
|
565
625
|
* ```
|
|
566
626
|
**/
|
|
567
627
|
public satisfies(required: FeatureVersion): boolean {
|
|
568
|
-
const {major, minor} = this
|
|
569
|
-
const {major: rMajor, minor: rMinor} = required
|
|
628
|
+
const { major, minor } = this
|
|
629
|
+
const { major: rMajor, minor: rMinor } = required
|
|
570
630
|
return rMajor == major && (
|
|
571
631
|
major == 0
|
|
572
632
|
? rMinor == minor
|
|
@@ -580,7 +640,7 @@ export class FeatureVersion {
|
|
|
580
640
|
* of compatibility, so those will just return the same thing as `this.toString()`.
|
|
581
641
|
*/
|
|
582
642
|
public get series() {
|
|
583
|
-
const {major} = this
|
|
643
|
+
const { major } = this
|
|
584
644
|
return major > 0 ? `${major}.x` : String(this)
|
|
585
645
|
}
|
|
586
646
|
|
|
@@ -650,7 +710,7 @@ export class FeatureUrl {
|
|
|
650
710
|
public readonly name: string,
|
|
651
711
|
public readonly version: FeatureVersion,
|
|
652
712
|
public readonly element?: string,
|
|
653
|
-
) {}
|
|
713
|
+
) { }
|
|
654
714
|
|
|
655
715
|
/// Parse a spec URL or throw
|
|
656
716
|
public static parse(input: string, node?: ASTNode): FeatureUrl {
|
|
@@ -668,7 +728,7 @@ export class FeatureUrl {
|
|
|
668
728
|
if (!name) {
|
|
669
729
|
throw ERRORS.INVALID_LINK_IDENTIFIER.err(`Missing feature name component in feature url '${url}'`, { nodes: node })
|
|
670
730
|
}
|
|
671
|
-
const element = url.hash ? url.hash.slice(1): undefined
|
|
731
|
+
const element = url.hash ? url.hash.slice(1) : undefined
|
|
672
732
|
url.hash = ''
|
|
673
733
|
url.search = ''
|
|
674
734
|
url.password = ''
|
|
@@ -690,7 +750,7 @@ export class FeatureUrl {
|
|
|
690
750
|
*/
|
|
691
751
|
public satisfies(requested: FeatureUrl): boolean {
|
|
692
752
|
return requested.identity === this.identity &&
|
|
693
|
-
|
|
753
|
+
this.version.satisfies(requested.version)
|
|
694
754
|
}
|
|
695
755
|
|
|
696
756
|
public equals(other: FeatureUrl) {
|
package/src/definitions.ts
CHANGED
|
@@ -1078,16 +1078,7 @@ export class CoreFeatures {
|
|
|
1078
1078
|
isImported: false,
|
|
1079
1079
|
} : undefined;
|
|
1080
1080
|
} else {
|
|
1081
|
-
|
|
1082
|
-
if (directFeature && isDirective) {
|
|
1083
|
-
return {
|
|
1084
|
-
feature: directFeature,
|
|
1085
|
-
nameInFeature: directFeature.imports.find(imp => imp.as === `@${element.name}`)?.name.slice(1) ?? element.name,
|
|
1086
|
-
isImported: true,
|
|
1087
|
-
};
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
// Let's see if it's an import. If not, it's not associated to a declared feature.
|
|
1081
|
+
// Let's first see if it's an import, as this would take precedence over directive implicitely named like their feature.
|
|
1091
1082
|
const importName = isDirective ? '@' + element.name : element.name;
|
|
1092
1083
|
const allFeatures = [this.coreItself, ...this.byIdentity.values()];
|
|
1093
1084
|
for (const feature of allFeatures) {
|
|
@@ -1101,6 +1092,17 @@ export class CoreFeatures {
|
|
|
1101
1092
|
}
|
|
1102
1093
|
}
|
|
1103
1094
|
}
|
|
1095
|
+
|
|
1096
|
+
// Otherwise, this may be the special directive having the same name as its feature.
|
|
1097
|
+
const directFeature = this.byAlias.get(element.name);
|
|
1098
|
+
if (directFeature && isDirective) {
|
|
1099
|
+
return {
|
|
1100
|
+
feature: directFeature,
|
|
1101
|
+
nameInFeature: directFeature.imports.find(imp => imp.as === `@${element.name}`)?.name.slice(1) ?? element.name,
|
|
1102
|
+
isImported: true,
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1104
1106
|
return undefined;
|
|
1105
1107
|
}
|
|
1106
1108
|
}
|
|
@@ -1113,22 +1115,22 @@ const graphQLBuiltInDirectivesSpecifications: readonly DirectiveSpecification[]
|
|
|
1113
1115
|
createDirectiveSpecification({
|
|
1114
1116
|
name: 'include',
|
|
1115
1117
|
locations: [DirectiveLocation.FIELD, DirectiveLocation.FRAGMENT_SPREAD, DirectiveLocation.INLINE_FRAGMENT],
|
|
1116
|
-
|
|
1118
|
+
args: [{ name: 'if', type: (schema) => new NonNullType(schema.booleanType()) }],
|
|
1117
1119
|
}),
|
|
1118
1120
|
createDirectiveSpecification({
|
|
1119
1121
|
name: 'skip',
|
|
1120
1122
|
locations: [DirectiveLocation.FIELD, DirectiveLocation.FRAGMENT_SPREAD, DirectiveLocation.INLINE_FRAGMENT],
|
|
1121
|
-
|
|
1123
|
+
args: [{ name: 'if', type: (schema) => new NonNullType(schema.booleanType()) }],
|
|
1122
1124
|
}),
|
|
1123
1125
|
createDirectiveSpecification({
|
|
1124
1126
|
name: 'deprecated',
|
|
1125
1127
|
locations: [DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.ENUM_VALUE, DirectiveLocation.ARGUMENT_DEFINITION, DirectiveLocation.INPUT_FIELD_DEFINITION],
|
|
1126
|
-
|
|
1128
|
+
args: [{ name: 'reason', type: (schema) => schema.stringType(), defaultValue: 'No longer supported' }],
|
|
1127
1129
|
}),
|
|
1128
1130
|
createDirectiveSpecification({
|
|
1129
1131
|
name: 'specifiedBy',
|
|
1130
1132
|
locations: [DirectiveLocation.SCALAR],
|
|
1131
|
-
|
|
1133
|
+
args: [{ name: 'url', type: (schema) => new NonNullType(schema.stringType()) }],
|
|
1132
1134
|
}),
|
|
1133
1135
|
// Note that @defer and @stream are unconditionally added to `Schema` even if they are technically "optional" built-in. _But_,
|
|
1134
1136
|
// the `Schema#toGraphQLJSSchema` method has an option to decide if @defer/@stream should be included or not in the resulting
|
|
@@ -1136,13 +1138,10 @@ const graphQLBuiltInDirectivesSpecifications: readonly DirectiveSpecification[]
|
|
|
1136
1138
|
createDirectiveSpecification({
|
|
1137
1139
|
name: 'defer',
|
|
1138
1140
|
locations: [DirectiveLocation.FRAGMENT_SPREAD, DirectiveLocation.INLINE_FRAGMENT],
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
],
|
|
1144
|
-
errors: [],
|
|
1145
|
-
})
|
|
1141
|
+
args: [
|
|
1142
|
+
{ name: 'label', type: (schema) => schema.stringType() },
|
|
1143
|
+
{ name: 'if', type: (schema) => new NonNullType(schema.booleanType()), defaultValue: true },
|
|
1144
|
+
],
|
|
1146
1145
|
}),
|
|
1147
1146
|
// Adding @stream too so that it's know and we don't error out if it is queries. It feels like it would be weird to do so for @stream but not
|
|
1148
1147
|
// @defer when both are defined in the same spec. That said, that does *not* mean we currently _implement_ @stream, we don't, and so putting
|
|
@@ -1150,14 +1149,11 @@ const graphQLBuiltInDirectivesSpecifications: readonly DirectiveSpecification[]
|
|
|
1150
1149
|
createDirectiveSpecification({
|
|
1151
1150
|
name: 'stream',
|
|
1152
1151
|
locations: [DirectiveLocation.FIELD],
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
],
|
|
1159
|
-
errors: [],
|
|
1160
|
-
})
|
|
1152
|
+
args: [
|
|
1153
|
+
{ name: 'label', type: (schema) => schema.stringType() },
|
|
1154
|
+
{ name: 'initialCount', type: (schema) => schema.intType(), defaultValue: 0 },
|
|
1155
|
+
{ name: 'if', type: (schema) => new NonNullType(schema.booleanType()), defaultValue: true },
|
|
1156
|
+
],
|
|
1161
1157
|
}),
|
|
1162
1158
|
];
|
|
1163
1159
|
|
|
@@ -1457,6 +1453,16 @@ export class Schema {
|
|
|
1457
1453
|
return this._builtInTypes.get('ID')! as ScalarType;
|
|
1458
1454
|
}
|
|
1459
1455
|
|
|
1456
|
+
builtInScalarTypes(): ScalarType[] {
|
|
1457
|
+
return [
|
|
1458
|
+
this.intType(),
|
|
1459
|
+
this.floatType(),
|
|
1460
|
+
this.stringType(),
|
|
1461
|
+
this.booleanType(),
|
|
1462
|
+
this.idType(),
|
|
1463
|
+
];
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1460
1466
|
addType<T extends NamedType>(type: T): T {
|
|
1461
1467
|
const existing = this.type(type.name);
|
|
1462
1468
|
if (existing) {
|