@apollo/federation-internals
Advanced tools
Comparing version 2.0.0-alpha.1 to 2.0.0-alpha.2
@@ -57,3 +57,3 @@ import { ASTNode, StringValueNode } from "graphql"; | ||
versions(): FeatureVersion[]; | ||
latest(): T | undefined; | ||
latest(): T; | ||
} | ||
@@ -60,0 +60,0 @@ export declare class FeatureVersion { |
@@ -9,2 +9,3 @@ "use strict"; | ||
const core_schema_1 = require("@apollo/core-schema"); | ||
const utils_1 = require("./utils"); | ||
exports.coreIdentity = 'https://specs.apollo.dev/core'; | ||
@@ -213,2 +214,3 @@ const ErrCoreCheckFailed = (causes) => (0, core_schema_1.err)('CheckFailed', { | ||
latest() { | ||
(0, utils_1.assert)(this._definitions.length > 0, 'Trying to get latest when no definitions exist'); | ||
return this._definitions[0]; | ||
@@ -215,0 +217,0 @@ } |
@@ -42,5 +42,5 @@ "use strict"; | ||
let currentIndentLevel = 0; | ||
let currentIdentation = ''; | ||
let currentIndentation = ''; | ||
let maxLoggerNameLength = 0; | ||
let createdLoggers = []; | ||
const createdLoggers = []; | ||
function newDebugLogger(name) { | ||
@@ -62,3 +62,3 @@ const enabled = isEnabled(name); | ||
currentIndentLevel++; | ||
currentIdentation = indentString(currentIndentLevel); | ||
currentIndentation = indentString(currentIndentLevel); | ||
} | ||
@@ -68,3 +68,3 @@ function decreaseIndentation() { | ||
currentIndentLevel--; | ||
currentIdentation = indentString(currentIndentLevel); | ||
currentIndentation = indentString(currentIndentLevel); | ||
} | ||
@@ -89,3 +89,3 @@ } | ||
doLog(str) { | ||
const indent = this.header + currentIdentation; | ||
const indent = this.header + currentIndentation; | ||
const withIndentedNewlines = str.replace(/\n/g, '\n' + indent + ' '); | ||
@@ -92,0 +92,0 @@ console.log(indent + withIndentedNewlines); |
@@ -93,4 +93,6 @@ import { ArgumentNode, ASTNode, DirectiveLocationEnum, DirectiveNode, DocumentNode, GraphQLError, GraphQLSchema, TypeNode, VariableDefinitionNode, VariableNode } from "graphql"; | ||
sourceAST?: ASTNode; | ||
schema(): Schema | undefined; | ||
get parent(): TParent | undefined; | ||
schema(): Schema; | ||
protected schemaInternal(): Schema | undefined; | ||
get parent(): TParent; | ||
isAttached(): boolean; | ||
private setParent; | ||
@@ -128,3 +130,4 @@ protected onAttached(): void; | ||
protected checkUpdate(addedElement?: { | ||
schema(): Schema | undefined; | ||
schema(): Schema; | ||
isAttached(): boolean; | ||
}): void; | ||
@@ -256,3 +259,3 @@ } | ||
toAPISchema(): Schema; | ||
toGraphQLJSSchema(): GraphQLSchema; | ||
toGraphQLJSSchema(isSubgraph?: boolean): GraphQLSchema; | ||
get schemaDefinition(): SchemaDefinition; | ||
@@ -411,3 +414,4 @@ types<T extends NamedType>(kind?: T['kind'], includeNonGraphQLBuiltIns?: boolean): readonly T[]; | ||
protected constructor(_type: T); | ||
schema(): Schema | undefined; | ||
schema(): Schema; | ||
isAttached(): boolean; | ||
get ofType(): T; | ||
@@ -521,3 +525,3 @@ baseType(): NamedType; | ||
constructor(name: string, _args: TArgs); | ||
schema(): Schema | undefined; | ||
schema(): Schema; | ||
get definition(): DirectiveDefinition | undefined; | ||
@@ -524,0 +528,0 @@ arguments(includeDefaultValues?: boolean): Readonly<TArgs>; |
@@ -89,4 +89,4 @@ "use strict"; | ||
const subgraphType = subgraph.schema.type(type.name); | ||
const subraphItf = subgraph.schema.type(name); | ||
if (subgraphType && subraphItf) { | ||
const subgraphItf = subgraph.schema.type(name); | ||
if (subgraphType && subgraphItf) { | ||
subgraphType.addImplementedInterface(name); | ||
@@ -294,3 +294,3 @@ } | ||
const external = federation_1.federationBuiltIns.externalDirective(subgraph.schema); | ||
let accessor = function (type, fieldName) { | ||
const accessor = function (type, fieldName) { | ||
const field = type.field(fieldName); | ||
@@ -362,6 +362,21 @@ if (field) { | ||
} | ||
function isExternalOrHasExternalImplementations(field) { | ||
if (field.hasAppliedDirective(federation_1.externalDirectiveName)) { | ||
return true; | ||
} | ||
const parentType = field.parent; | ||
if ((0, definitions_1.isInterfaceType)(parentType)) { | ||
for (const implem of parentType.possibleRuntimeTypes()) { | ||
const fieldInImplem = implem.field(field.name); | ||
if (fieldInImplem && fieldInImplem.hasAppliedDirective(federation_1.externalDirectiveName)) { | ||
return true; | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
function selectsNonExternalLeafField(selection) { | ||
return selection.selections().some(s => { | ||
if (s.kind === 'FieldSelection') { | ||
if (s.field.definition.hasAppliedDirective(federation_1.externalDirectiveName)) { | ||
if (isExternalOrHasExternalImplementations(s.field.definition)) { | ||
return false; | ||
@@ -380,3 +395,3 @@ } | ||
if (selection.kind === 'FieldSelection') { | ||
if (selection.field.definition.hasAppliedDirective(federation_1.externalDirectiveName)) { | ||
if (isExternalOrHasExternalImplementations(selection.field.definition)) { | ||
newSelectionSet.add(selection); | ||
@@ -383,0 +398,0 @@ continue; |
@@ -27,4 +27,4 @@ import { BuiltIns, Schema, DirectiveDefinition, NamedType, Directive, FieldDefinition, CompositeType, InputFieldDefinition } from "./definitions"; | ||
}>; | ||
extendsDirective(schema: Schema): DirectiveDefinition<{}>; | ||
externalDirective(schema: Schema): DirectiveDefinition<{}>; | ||
extendsDirective(schema: Schema): DirectiveDefinition<Record<string, never>>; | ||
externalDirective(schema: Schema): DirectiveDefinition<Record<string, never>>; | ||
requiresDirective(schema: Schema): DirectiveDefinition<{ | ||
@@ -59,11 +59,9 @@ fields: any; | ||
private readonly subgraphs; | ||
private idx; | ||
add(subgraph: Subgraph): Subgraph; | ||
add(name: string, url: string, schema: Schema | DocumentNode | string): Subgraph; | ||
get(name: string): Subgraph | undefined; | ||
getByIdx(idx: number): Subgraph; | ||
size(): number; | ||
names(): readonly string[]; | ||
values(): readonly Subgraph[]; | ||
[Symbol.iterator](): IterableIterator<Subgraph>; | ||
[Symbol.iterator](): Generator<Subgraph, void, unknown>; | ||
toString(): string; | ||
@@ -70,0 +68,0 @@ } |
@@ -76,3 +76,14 @@ "use strict"; | ||
if (selection.selectionSet) { | ||
validateFieldSetSelections(directiveName, selection.selectionSet, hasExternalInParents || isExternal, externalTester, externalFieldCoordinatesCollector, allowOnNonExternalLeafFields); | ||
let newHasExternalInParents = hasExternalInParents || isExternal; | ||
const parentType = field.parent; | ||
if (!newHasExternalInParents && (0, definitions_1.isInterfaceType)(parentType)) { | ||
for (const implem of parentType.possibleRuntimeTypes()) { | ||
const fieldInImplem = implem.field(field.name); | ||
if (fieldInImplem && externalTester.isExternal(fieldInImplem)) { | ||
newHasExternalInParents = true; | ||
break; | ||
} | ||
} | ||
} | ||
validateFieldSetSelections(directiveName, selection.selectionSet, newHasExternalInParents, externalTester, externalFieldCoordinatesCollector, allowOnNonExternalLeafFields); | ||
} | ||
@@ -199,3 +210,3 @@ } | ||
const queryType = queryRoot ? queryRoot.type : schema.addType(new definitions_1.ObjectType("Query")); | ||
let entityField = queryType.field(exports.entitiesFieldName); | ||
const entityField = queryType.field(exports.entitiesFieldName); | ||
if (hasEntities) { | ||
@@ -242,5 +253,8 @@ const anyType = schema.type(exports.anyTypeName); | ||
validateAllFieldSet(this.providesDirective(schema), field => { | ||
if (externalTester.isExternal(field)) { | ||
throw new graphql_1.GraphQLError(`Cannot have both @provides and @external on field "${field.coordinate}"`, field.sourceAST); | ||
} | ||
const type = (0, definitions_1.baseType)(field.type); | ||
if (!(0, definitions_1.isObjectType)(type)) { | ||
throw new graphql_1.GraphQLError(`Invalid @provides directive on field "${field.coordinate}": field has type "${field.type}" which is not an Object Type`, field.sourceAST); | ||
if (!(0, definitions_1.isCompositeType)(type)) { | ||
throw new graphql_1.GraphQLError(`Invalid @provides directive on field "${field.coordinate}": field has type "${field.type}" which is not a Composite Type`, field.sourceAST); | ||
} | ||
@@ -282,3 +296,3 @@ return type; | ||
document = super.maybeUpdateSubgraphDocument(schema, document); | ||
let definitions = document.definitions.concat(); | ||
const definitions = document.definitions.concat(); | ||
for (const directiveName of FEDERATION_DIRECTIVES) { | ||
@@ -377,7 +391,4 @@ const directive = schema.directive(directiveName); | ||
constructor() { | ||
this.subgraphs = []; | ||
this.subgraphs = new utils_1.OrderedMap(); | ||
} | ||
idx(name) { | ||
return this.subgraphs.findIndex(s => s.name === name); | ||
} | ||
add(subgraphOrName, url, schema) { | ||
@@ -390,31 +401,27 @@ const toAdd = typeof subgraphOrName === 'string' | ||
} | ||
const idx = this.idx(toAdd.name); | ||
if (idx >= 0) { | ||
if (this.subgraphs.has(toAdd.name)) { | ||
throw new Error(`A subgraph named ${toAdd.name} already exists` + (toAdd.url ? ` (with url '${toAdd.url}')` : '')); | ||
} | ||
this.subgraphs.push(toAdd); | ||
this.subgraphs.sort(); | ||
this.subgraphs.add(toAdd.name, toAdd); | ||
return toAdd; | ||
} | ||
get(name) { | ||
const idx = this.idx(name); | ||
return idx >= 0 ? this.subgraphs[idx] : undefined; | ||
return this.subgraphs.get(name); | ||
} | ||
getByIdx(idx) { | ||
return this.subgraphs[idx]; | ||
} | ||
size() { | ||
return this.subgraphs.length; | ||
return this.subgraphs.size; | ||
} | ||
names() { | ||
return this.subgraphs.map(s => s.name); | ||
return this.subgraphs.keys(); | ||
} | ||
values() { | ||
return this.subgraphs; | ||
} | ||
[Symbol.iterator]() { | ||
return this.subgraphs.values(); | ||
} | ||
*[Symbol.iterator]() { | ||
for (const subgraph of this.subgraphs) { | ||
yield subgraph; | ||
} | ||
} | ||
toString() { | ||
return '[' + this.subgraphs.map(s => s.name).join(', ') + ']'; | ||
return '[' + this.subgraphs.keys().join(', ') + ']'; | ||
} | ||
@@ -421,0 +428,0 @@ } |
@@ -7,3 +7,3 @@ import { FeatureDefinition, FeatureDefinitions, FeatureVersion } from "./coreSpec"; | ||
addElementsToSchema(schema: Schema): void; | ||
inaccessibleDirective(schema: Schema): DirectiveDefinition<{}>; | ||
inaccessibleDirective(schema: Schema): DirectiveDefinition<Record<string, never>>; | ||
} | ||
@@ -10,0 +10,0 @@ export declare const INACCESSIBLE_VERSIONS: FeatureDefinitions<InaccessibleSpecDefinition>; |
@@ -13,3 +13,3 @@ "use strict"; | ||
addElementsToSchema(schema) { | ||
this.addDirective(schema, 'inacessible').addLocations('FIELD_DEFINITION', 'OBJECT', 'INTERFACE', 'UNION'); | ||
this.addDirective(schema, 'inaccessible').addLocations('FIELD_DEFINITION', 'OBJECT', 'INTERFACE', 'UNION'); | ||
} | ||
@@ -28,9 +28,9 @@ inaccessibleDirective(schema) { | ||
} | ||
const inacessibleFeature = coreFeatures.getByIdentity(exports.inaccessibleIdentity); | ||
if (!inacessibleFeature) { | ||
const inaccessibleFeature = coreFeatures.getByIdentity(exports.inaccessibleIdentity); | ||
if (!inaccessibleFeature) { | ||
return; | ||
} | ||
const inaccessibleSpec = exports.INACCESSIBLE_VERSIONS.find(inacessibleFeature.url.version); | ||
const inaccessibleSpec = exports.INACCESSIBLE_VERSIONS.find(inaccessibleFeature.url.version); | ||
if (!inaccessibleSpec) { | ||
throw new graphql_1.GraphQLError(`Cannot remove inacessible elements: the schema uses unsupported inacessible spec version ${inacessibleFeature.url.version} (supported versions: ${exports.INACCESSIBLE_VERSIONS.versions().join(', ')})`); | ||
throw new graphql_1.GraphQLError(`Cannot remove inaccessible elements: the schema uses unsupported inaccessible spec version ${inaccessibleFeature.url.version} (supported versions: ${exports.INACCESSIBLE_VERSIONS.versions().join(', ')})`); | ||
} | ||
@@ -37,0 +37,0 @@ const inaccessibleDirective = inaccessibleSpec.inaccessibleDirective(schema); |
@@ -452,3 +452,3 @@ "use strict"; | ||
const key = toAdd.key(); | ||
let existing = this._selections.get(key); | ||
const existing = this._selections.get(key); | ||
if (existing) { | ||
@@ -455,0 +455,0 @@ for (const existingSelection of existing) { |
@@ -11,3 +11,3 @@ import { DirectiveDefinition, NamedType, Schema, SchemaRootKind } from "./definitions"; | ||
showNonGraphQLBuiltIns: boolean; | ||
noDesciptions: boolean; | ||
noDescriptions: boolean; | ||
}; | ||
@@ -14,0 +14,0 @@ export declare const defaultPrintOptions: Options; |
@@ -13,3 +13,3 @@ "use strict"; | ||
showNonGraphQLBuiltIns: false, | ||
noDesciptions: false, | ||
noDescriptions: false, | ||
}; | ||
@@ -126,3 +126,3 @@ function orderPrintedDefinitions(options) { | ||
function printDescription(element, options, indentation = '', firstInBlock = true) { | ||
if (element.description === undefined || options.noDesciptions) { | ||
if (element.description === undefined || options.noDescriptions) { | ||
return ''; | ||
@@ -212,4 +212,4 @@ } | ||
function printField(field, options) { | ||
let args = field.kind == 'FieldDefinition' ? printArgs(field.arguments(), options, options.indentString) : ''; | ||
let defaultValue = field.kind === 'InputFieldDefinition' && field.defaultValue !== undefined | ||
const args = field.kind == 'FieldDefinition' ? printArgs(field.arguments(), options, options.indentString) : ''; | ||
const defaultValue = field.kind === 'InputFieldDefinition' && field.defaultValue !== undefined | ||
? ' = ' + (0, values_1.valueToString)(field.defaultValue, field.type) | ||
@@ -216,0 +216,0 @@ : ''; |
@@ -29,3 +29,3 @@ "use strict"; | ||
function didYouMean(suggestions) { | ||
let message = ' Did you mean '; | ||
const message = ' Did you mean '; | ||
const quotedSuggestions = suggestions.map((x) => `"${x}"`); | ||
@@ -32,0 +32,0 @@ switch (suggestions.length) { |
@@ -6,2 +6,17 @@ export declare function assert(condition: any, message: string | (() => string)): asserts condition; | ||
} | ||
export declare class OrderedMap<K, V> { | ||
private _keys; | ||
private _values; | ||
private _compareFn; | ||
private static defaultCompareFn; | ||
constructor(compareFn?: (a: K, b: K) => number); | ||
add(key: K, value: V): void; | ||
get(key: K): V | undefined; | ||
has(key: K): boolean; | ||
get size(): number; | ||
keys(): K[]; | ||
values(): V[]; | ||
private insertKeyInOrder; | ||
[Symbol.iterator](): Generator<V, void, unknown>; | ||
} | ||
export declare function arrayEquals<T>(a: readonly T[], b: readonly T[], equalFct?: (e1: T, e2: T) => boolean): boolean; | ||
@@ -8,0 +23,0 @@ export declare function firstOf<T>(iterable: Iterable<T>): T | undefined; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.copyWitNewLength = exports.MapWithCachedArrays = exports.setValues = exports.mapEntries = exports.mapKeys = exports.mapValues = exports.firstOf = exports.arrayEquals = exports.MultiMap = exports.assertUnreachable = exports.assert = void 0; | ||
exports.copyWitNewLength = exports.MapWithCachedArrays = exports.setValues = exports.mapEntries = exports.mapKeys = exports.mapValues = exports.firstOf = exports.arrayEquals = exports.OrderedMap = exports.MultiMap = exports.assertUnreachable = exports.assert = void 0; | ||
function assert(condition, message) { | ||
@@ -27,2 +27,65 @@ if (!condition) { | ||
exports.MultiMap = MultiMap; | ||
class OrderedMap { | ||
constructor(compareFn = OrderedMap.defaultCompareFn) { | ||
this._keys = []; | ||
this._values = new Map(); | ||
this._compareFn = compareFn; | ||
} | ||
static defaultCompareFn(a, b) { | ||
if (a < b) { | ||
return -1; | ||
} | ||
else if (b < a) { | ||
return 1; | ||
} | ||
return 0; | ||
} | ||
add(key, value) { | ||
if (!this._values.has(key)) { | ||
this.insertKeyInOrder(key); | ||
} | ||
this._values.set(key, value); | ||
} | ||
get(key) { | ||
return this._values.get(key); | ||
} | ||
has(key) { | ||
return this._values.has(key); | ||
} | ||
get size() { | ||
return this._keys.length; | ||
} | ||
keys() { | ||
return this._keys; | ||
} | ||
values() { | ||
return this._keys.map(key => { | ||
const v = this._values.get(key); | ||
assert(v, 'value for known key not found in OrderedMap'); | ||
return v; | ||
}); | ||
} | ||
insertKeyInOrder(key) { | ||
let lower = 0; | ||
let upper = this._keys.length - 1; | ||
while (lower <= upper) { | ||
const middle = Math.floor((upper + lower) / 2); | ||
if (this._compareFn(this._keys[middle], key) < 0) { | ||
lower = middle + 1; | ||
} | ||
else { | ||
upper = middle - 1; | ||
} | ||
} | ||
this._keys = this._keys.slice(0, lower).concat(key).concat(this._keys.slice(lower)); | ||
} | ||
*[Symbol.iterator]() { | ||
for (let i = 0; i < this._keys.length; i += 1) { | ||
const v = this._values.get(this._keys[i]); | ||
assert(v, 'value for known key not found in OrderedMap'); | ||
yield v; | ||
} | ||
} | ||
} | ||
exports.OrderedMap = OrderedMap; | ||
function arrayEquals(a, b, equalFct) { | ||
@@ -29,0 +92,0 @@ if (a === b) { |
@@ -44,3 +44,3 @@ "use strict"; | ||
var _a; | ||
let valueType = expectedType ? (_a = expectedType.field(k)) === null || _a === void 0 ? void 0 : _a.type : undefined; | ||
const valueType = expectedType ? (_a = expectedType.field(k)) === null || _a === void 0 ? void 0 : _a.type : undefined; | ||
return `${k}: ${valueToString(v[k], valueType)}`; | ||
@@ -182,3 +182,2 @@ }).join(', ') + '}'; | ||
function valueToAST(value, type) { | ||
var _a; | ||
if (value === undefined) { | ||
@@ -251,3 +250,3 @@ return undefined; | ||
} | ||
if (type === ((_a = type.schema()) === null || _a === void 0 ? void 0 : _a.idType()) && integerStringRegExp.test(value)) { | ||
if (type === type.schema().idType() && integerStringRegExp.test(value)) { | ||
return { kind: graphql_1.Kind.INT, value: value }; | ||
@@ -254,0 +253,0 @@ } |
{ | ||
"name": "@apollo/federation-internals", | ||
"version": "2.0.0-alpha.1", | ||
"version": "2.0.0-alpha.2", | ||
"description": "Apollo Federation internal utilities", | ||
@@ -26,4 +26,3 @@ "main": "dist/index.js", | ||
"dependencies": { | ||
"@apollo/core-schema": "^0.1.0", | ||
"@types/jest": "^26.0.23", | ||
"@apollo/core-schema": "^0.2.0", | ||
"chalk": "^4.1.0", | ||
@@ -36,8 +35,8 @@ "js-levenshtein": "^1.1.6" | ||
"peerDependencies": { | ||
"graphql": "^14.5.0 || ^15.0.0" | ||
"graphql": "^15.7.0" | ||
}, | ||
"devDependencies": { | ||
"@types/js-levenshtein": "^1.1.0" | ||
"@types/js-levenshtein": "1.1.0" | ||
}, | ||
"gitHead": "5f7184829607024343fb8b8ad0ac4e122407cd3a" | ||
"gitHead": "9265ba09ec5620b931861a44603d7d5f7d4688f2" | ||
} |
@@ -223,3 +223,3 @@ import { | ||
test('removal of all inacessible elements of a schema', () => { | ||
test('removal of all inaccessible elements of a schema', () => { | ||
const schema = parseSchema(` | ||
@@ -411,3 +411,3 @@ schema @foo { | ||
bestProducts: [Product!]! | ||
"""Finds a product by ID""" | ||
@@ -424,3 +424,3 @@ product( | ||
description: String! | ||
""" | ||
@@ -427,0 +427,0 @@ Number of pages in the book. Good so the customer knows its buying a 1000 page book for instance |
@@ -62,4 +62,4 @@ // Make this file a module (See: https://github.com/microsoft/TypeScript/issues/17736) | ||
let pass: boolean = true; | ||
let messages: string[] = []; | ||
let pass = true; | ||
const messages: string[] = []; | ||
for (let i = 0; i < expected.length; i++) { | ||
@@ -66,0 +66,0 @@ const exp = expected[i]; |
@@ -7,3 +7,3 @@ import { ObjectType } from '../definitions'; | ||
it(`removes @inaccessible fields`, () => { | ||
let schema = buildSchema(` | ||
const schema = buildSchema(` | ||
directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA | ||
@@ -40,3 +40,3 @@ | ||
it(`removes @inaccessible object types`, () => { | ||
let schema = buildSchema(` | ||
const schema = buildSchema(` | ||
directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA | ||
@@ -75,3 +75,3 @@ | ||
it(`removes @inaccessible interface types`, () => { | ||
let schema = buildSchema(` | ||
const schema = buildSchema(` | ||
directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA | ||
@@ -116,3 +116,3 @@ | ||
it(`removes @inaccessible union types`, () => { | ||
let schema = buildSchema(` | ||
const schema = buildSchema(` | ||
directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA | ||
@@ -157,3 +157,3 @@ | ||
it(`throws when a field returning an @inaccessible type isn't marked @inaccessible itself`, () => { | ||
let schema = buildSchema(` | ||
const schema = buildSchema(` | ||
directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA | ||
@@ -194,3 +194,3 @@ | ||
it(`removes @inaccessible query root type`, () => { | ||
let schema = buildSchema(` | ||
const schema = buildSchema(` | ||
directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA | ||
@@ -230,3 +230,3 @@ | ||
it(`removes @inaccessible mutation root type`, () => { | ||
let schema = buildSchema(` | ||
const schema = buildSchema(` | ||
directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA | ||
@@ -269,3 +269,3 @@ | ||
it(`removes @inaccessible subscription root type`, () => { | ||
let schema = buildSchema(` | ||
const schema = buildSchema(` | ||
directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA | ||
@@ -272,0 +272,0 @@ |
@@ -60,3 +60,3 @@ import { | ||
// - we don't get any custom coercion (but neither is buildSchema in graphQL-js anyway). | ||
// 2) type validation. | ||
// 2) type validation. | ||
return value ? valueFromASTUntyped(value) : undefined; | ||
@@ -95,3 +95,3 @@ } | ||
definitionNode, | ||
schema.schemaDefinition, | ||
schema.schemaDefinition, | ||
schema.schemaDefinition.newExtension()); | ||
@@ -305,3 +305,3 @@ break; | ||
function buildFieldDefinitionInner(fieldNode: FieldDefinitionNode, field: FieldDefinition<any>) { | ||
const type = buildTypeReferenceFromAST(fieldNode.type, field.schema()!); | ||
const type = buildTypeReferenceFromAST(fieldNode.type, field.schema()); | ||
field.type = ensureOutputType(type, field.coordinate, fieldNode); | ||
@@ -352,3 +352,3 @@ for (const inputValueDef of fieldNode.arguments ?? []) { | ||
function buildArgumentDefinitionInner(inputNode: InputValueDefinitionNode, arg: ArgumentDefinition<any>) { | ||
const type = buildTypeReferenceFromAST(inputNode.type, arg.schema()!); | ||
const type = buildTypeReferenceFromAST(inputNode.type, arg.schema()); | ||
arg.type = ensureInputType(type, arg.coordinate, inputNode); | ||
@@ -362,3 +362,3 @@ arg.defaultValue = buildValue(inputNode.defaultValue); | ||
function buildInputFieldDefinitionInner(fieldNode: InputValueDefinitionNode, field: InputFieldDefinition) { | ||
const type = buildTypeReferenceFromAST(fieldNode.type, field.schema()!); | ||
const type = buildTypeReferenceFromAST(fieldNode.type, field.schema()); | ||
field.type = ensureInputType(type, field.coordinate, fieldNode); | ||
@@ -365,0 +365,0 @@ field.defaultValue = buildValue(fieldNode.defaultValue); |
@@ -6,2 +6,3 @@ import { ASTNode, DirectiveLocation, GraphQLError, StringValueNode } from "graphql"; | ||
import { err } from '@apollo/core-schema'; | ||
import { assert } from './utils'; | ||
@@ -52,3 +53,3 @@ export const coreIdentity = 'https://specs.apollo.dev/core'; | ||
isSpecType(type: NamedType): boolean { | ||
const nameInSchema = this.nameInSchema(type.schema()!); | ||
const nameInSchema = this.nameInSchema(type.schema()); | ||
return nameInSchema !== undefined && type.name.startsWith(`${nameInSchema}__`); | ||
@@ -58,3 +59,3 @@ } | ||
isSpecDirective(directive: DirectiveDefinition): boolean { | ||
const nameInSchema = this.nameInSchema(directive.schema()!); | ||
const nameInSchema = this.nameInSchema(directive.schema()); | ||
return nameInSchema != undefined && (directive.name === nameInSchema || directive.name.startsWith(`${nameInSchema}__`)); | ||
@@ -133,7 +134,7 @@ } | ||
const featureArg = definition.argument('feature'); | ||
if (!featureArg || !sameType(featureArg.type!, new NonNullType(directive.schema()!.stringType()))) { | ||
if (!featureArg || !sameType(featureArg.type!, new NonNullType(directive.schema().stringType()))) { | ||
return false; | ||
} | ||
const asArg = definition.argument('as'); | ||
if (asArg && !sameType(asArg.type!, directive.schema()!.stringType())) { | ||
if (asArg && !sameType(asArg.type!, directive.schema().stringType())) { | ||
return false; | ||
@@ -270,3 +271,4 @@ } | ||
latest(): T | undefined { | ||
latest(): T { | ||
assert(this._definitions.length > 0, 'Trying to get latest when no definitions exist'); | ||
return this._definitions[0]; | ||
@@ -289,3 +291,3 @@ } | ||
* expect(FeatureVersion.parse('v0.1')).toEqual(new FeatureVersion(0, 1)) | ||
* expect(FeatureVersion.parse("v987.65432")).toEqual(new FeatureVersion(987, 65432)) | ||
* expect(FeatureVersion.parse("v987.65432")).toEqual(new FeatureVersion(987, 65432)) | ||
* ``` | ||
@@ -336,3 +338,3 @@ */ | ||
* | ||
* Be aware that this ordering does *not* imply compatibility. For example, `FeatureVersion(2, 0) > FeatureVersion(1, 9)`, | ||
* Be aware that this ordering does *not* imply compatibility. For example, `FeatureVersion(2, 0) > FeatureVersion(1, 9)`, | ||
* but an implementation of `FeatureVersion(2, 0)` *cannot* satisfy a request for `FeatureVersion(1, 9)`. To check for | ||
@@ -372,3 +374,3 @@ * version compatibility, use [the `satisfies` method](#satisfies). | ||
* return the string version tag, like "v2.9" | ||
* | ||
* | ||
* @returns a version tag | ||
@@ -382,3 +384,3 @@ */ | ||
* return true iff this version is exactly equal to the provided version | ||
* | ||
* | ||
* @param other the version to compare | ||
@@ -436,3 +438,3 @@ * @returns true if versions are strictly equal | ||
* spec. | ||
* | ||
* | ||
* @param request | ||
@@ -439,0 +441,0 @@ */ |
@@ -43,6 +43,6 @@ // Simple debugging facility. | ||
let currentIndentLevel = 0; | ||
let currentIdentation = ''; | ||
let currentIndentation = ''; | ||
let maxLoggerNameLength = 0; | ||
let createdLoggers: DebugLogger[] = []; | ||
const createdLoggers: DebugLogger[] = []; | ||
@@ -53,3 +53,3 @@ export function newDebugLogger(name: string): DebugLogger { | ||
if (enabled) { | ||
// This next line is to avoid having JEST capture console logging if any logger is | ||
// This next line is to avoid having JEST capture console logging if any logger is | ||
// enabled, as this make things unreadable | ||
@@ -68,3 +68,3 @@ global.console = require('console'); | ||
currentIndentLevel++; | ||
currentIdentation = indentString(currentIndentLevel); | ||
currentIndentation = indentString(currentIndentLevel); | ||
} | ||
@@ -75,3 +75,3 @@ | ||
currentIndentLevel--; | ||
currentIdentation = indentString(currentIndentLevel); | ||
currentIndentation = indentString(currentIndentLevel); | ||
} | ||
@@ -112,3 +112,3 @@ } | ||
private doLog(str: string) { | ||
const indent = this.header + currentIdentation; | ||
const indent = this.header + currentIndentation; | ||
const withIndentedNewlines = str.replace(/\n/g, '\n' + indent + ' '); | ||
@@ -115,0 +115,0 @@ console.log(indent + withIndentedNewlines); |
@@ -78,3 +78,3 @@ import { | ||
// Next, we iterate on all types and add it to the proper subgraphs (along with any @key). | ||
// Note that we first add all types empty and populate the types next. This avoids having to care about the iteration | ||
// Note that we first add all types empty and populate the types next. This avoids having to care about the iteration | ||
// order if we have fields than depends on other types. | ||
@@ -132,4 +132,4 @@ for (const type of filteredTypes(supergraph, joinSpec, coreFeatures.coreDefinition)) { | ||
const subgraphType = subgraph.schema.type(type.name); | ||
const subraphItf = subgraph.schema.type(name); | ||
if (subgraphType && subraphItf) { | ||
const subgraphItf = subgraph.schema.type(name); | ||
if (subgraphType && subgraphItf) { | ||
(subgraphType as (ObjectType | InterfaceType)).addImplementedInterface(name); | ||
@@ -299,7 +299,7 @@ } | ||
function addSubgraphObjectOrInterfaceField( | ||
supergraphField: FieldDefinition<ObjectType | InterfaceType>, | ||
supergraphField: FieldDefinition<ObjectType | InterfaceType>, | ||
subgraph: Subgraph, | ||
encodedType?: string | ||
): FieldDefinition<ObjectType | InterfaceType> | undefined { | ||
const subgraphType = subgraph.schema.type(supergraphField.parent!.name); | ||
const subgraphType = subgraph.schema.type(supergraphField.parent.name); | ||
if (subgraphType) { | ||
@@ -324,3 +324,3 @@ const copiedType = encodedType | ||
): InputFieldDefinition | undefined { | ||
const subgraphType = subgraph.schema.type(supergraphField.parent!.name); | ||
const subgraphType = subgraph.schema.type(supergraphField.parent.name); | ||
if (subgraphType) { | ||
@@ -377,3 +377,3 @@ const copiedType = encodedType | ||
// If the key is on a type definition however, then we don't have that historical legacy, and so if the field is | ||
// not part of the subgprah, then it means that it is truly external (and composition validation will ensure that this | ||
// not part of the subgraph, then it means that it is truly external (and composition validation will ensure that this | ||
// is fine). | ||
@@ -407,3 +407,3 @@ // Note that this is called `forceNonExternal` because an extension key field might well be part of a @provides somewhere | ||
function addExternalFieldsFromDirectiveFieldSet( | ||
subgraph: Subgraph, | ||
subgraph: Subgraph, | ||
parentType: ObjectType | InterfaceType, | ||
@@ -416,3 +416,3 @@ directive: Directive<NamedType | FieldDefinition<CompositeType>, {fields: any}>, | ||
let accessor = function (type: CompositeType, fieldName: string): FieldDefinition<any> { | ||
const accessor = function (type: CompositeType, fieldName: string): FieldDefinition<any> { | ||
const field = type.field(fieldName); | ||
@@ -449,3 +449,3 @@ if (field) { | ||
// A subtlety here is that a type may implements multiple interfaces providing a given field, and the field may | ||
// not have the exact same defintion in all interface. So if we may have added the field in a previous loop | ||
// not have the exact same definition in all interface. So if we may have added the field in a previous loop | ||
// iteration, we need to check if we shouldn't update the field type. | ||
@@ -467,3 +467,3 @@ maybeUpdateFieldForInterface(typeField, field); | ||
function maybeUpdateFieldForInterface(toModify: FieldDefinition<ObjectType | InterfaceType>, itfField: FieldDefinition<InterfaceType>) { | ||
// Note that we only care about the field type because while graphql does not allow contravariance of args for field implmenations. | ||
// Note that we only care about the field type because while graphql does not allow contravariance of args for field implementations. | ||
// And while fed2 allow it when merging, this code doesn't run for fed2 generated supergraph, so this isn't a concern. | ||
@@ -486,3 +486,3 @@ if (!isSubtype(itfField.type!, toModify.type!)) { | ||
* it is that such provides have a negative impact on later query planning, because it sometimes make us to | ||
* try type-exploding some interfaces unecessarily. | ||
* try type-exploding some interfaces unnecessarily. | ||
*/ | ||
@@ -499,3 +499,3 @@ function removeNeedlessProvides(subgraph: Subgraph) { | ||
for (const providesApplication of field.appliedDirectivesOf(providesDirective)) { | ||
const selection = parseFieldSetArgument(fieldBaseType as ObjectType | InterfaceType, providesApplication); | ||
const selection = parseFieldSetArgument(fieldBaseType as CompositeType, providesApplication); | ||
if (selectsNonExternalLeafField(selection)) { | ||
@@ -513,2 +513,18 @@ providesApplication.remove(); | ||
function isExternalOrHasExternalImplementations(field: FieldDefinition<CompositeType>): boolean { | ||
if (field.hasAppliedDirective(externalDirectiveName)) { | ||
return true; | ||
} | ||
const parentType = field.parent; | ||
if (isInterfaceType(parentType)) { | ||
for (const implem of parentType.possibleRuntimeTypes()) { | ||
const fieldInImplem = implem.field(field.name); | ||
if (fieldInImplem && fieldInImplem.hasAppliedDirective(externalDirectiveName)) { | ||
return true; | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
function selectsNonExternalLeafField(selection: SelectionSet): boolean { | ||
@@ -518,3 +534,3 @@ return selection.selections().some(s => { | ||
// If it's external, we're good and don't need to recurse. | ||
if (s.field.definition.hasAppliedDirective(externalDirectiveName)) { | ||
if (isExternalOrHasExternalImplementations(s.field.definition)) { | ||
return false; | ||
@@ -534,3 +550,3 @@ } | ||
if (selection.kind === 'FieldSelection') { | ||
if (selection.field.definition.hasAppliedDirective(externalDirectiveName)) { | ||
if (isExternalOrHasExternalImplementations(selection.field.definition)) { | ||
// That field is external, so we can add the selection back entirely. | ||
@@ -537,0 +553,0 @@ newSelectionSet.add(selection); |
@@ -24,5 +24,6 @@ import { | ||
InterfaceType, | ||
InputFieldDefinition | ||
InputFieldDefinition, | ||
isCompositeType | ||
} from "./definitions"; | ||
import { assert } from "./utils"; | ||
import { assert, OrderedMap } from "./utils"; | ||
import { SDLValidationRule } from "graphql/validation/ValidationContext"; | ||
@@ -48,3 +49,3 @@ import { specifiedSDLRules } from "graphql/validation/specifiedRules"; | ||
export const providesDirectiveName = 'provides'; | ||
// TODO: so far, it seems we allow tag to appear without a corresponding definitio, so we add it as a built-in. | ||
// TODO: so far, it seems we allow tag to appear without a corresponding definition, so we add it as a built-in. | ||
// If we change our mind, we should change this. | ||
@@ -56,5 +57,5 @@ export const tagDirectiveName = 'tag'; | ||
const tagSpec = TAG_VERSIONS.latest()!; | ||
const tagSpec = TAG_VERSIONS.latest(); | ||
// We don't let user use this as a subgraph name. That allows us to use it in `query graphs` to name the source of roots | ||
// We don't let user use this as a subgraph name. That allows us to use it in `query graphs` to name the source of roots | ||
// in the "federated query graph" without worrying about conflict (see `FEDERATED_GRAPH_ROOT_SOURCE` in `querygraph.ts`). | ||
@@ -115,3 +116,3 @@ // (note that we could deal with this in other ways, but having a graph named '_' feels like a terrible idea anyway, so | ||
} | ||
// The field must be external if we don't allow non-external leaf fields, it's a leaft, and we haven't traversed an external field in parent chain leading here. | ||
// The field must be external if we don't allow non-external leaf fields, it's a leaf, and we haven't traversed an external field in parent chain leading here. | ||
const mustBeExternal = !selection.selectionSet && !allowOnNonExternalLeafFields && !hasExternalInParents; | ||
@@ -132,3 +133,17 @@ const isExternal = externalTester.isExternal(field); | ||
if (selection.selectionSet) { | ||
validateFieldSetSelections(directiveName, selection.selectionSet, hasExternalInParents || isExternal, externalTester, externalFieldCoordinatesCollector, allowOnNonExternalLeafFields); | ||
// When passing the 'hasExternalInParents', the field might be external himself, but we may also have | ||
// the case where the field parent is an interface and some implementation of the field are external, in | ||
// which case we should say we have an external on the path, because we may have one. | ||
let newHasExternalInParents = hasExternalInParents || isExternal; | ||
const parentType = field.parent; | ||
if (!newHasExternalInParents && isInterfaceType(parentType)) { | ||
for (const implem of parentType.possibleRuntimeTypes()) { | ||
const fieldInImplem = implem.field(field.name); | ||
if (fieldInImplem && externalTester.isExternal(fieldInImplem)) { | ||
newHasExternalInParents = true; | ||
break; | ||
} | ||
} | ||
} | ||
validateFieldSetSelections(directiveName, selection.selectionSet, newHasExternalInParents, externalTester, externalFieldCoordinatesCollector, allowOnNonExternalLeafFields); | ||
} | ||
@@ -194,3 +209,3 @@ } else { | ||
for (const application of definition.applications()) { | ||
const elt = application.parent! as TParent; | ||
const elt = application.parent as TParent; | ||
const type = targetTypeExtractor(elt); | ||
@@ -245,3 +260,3 @@ const targetDescription = targetDescriptionExtractor(elt); | ||
function isFieldSatisfyingInterface(field: FieldDefinition<ObjectType | InterfaceType>): boolean { | ||
return field.parent!.interfaces().some(itf => itf.field(field.name)); | ||
return field.parent.interfaces().some(itf => itf.field(field.name)); | ||
} | ||
@@ -265,3 +280,3 @@ | ||
// Note that we allow @key on interfaces in the definition to not break backward compatibility, because it has historically unfortunately be declared this way, but | ||
// @key is actually not suppported on interfaces at the moment, so if if is "used" then it is rejected. | ||
// @key is actually not supported on interfaces at the moment, so if if is "used" then it is rejected. | ||
const keyDirective = this.addBuiltInDirective(schema, keyDirectiveName) | ||
@@ -271,3 +286,3 @@ .addLocations('OBJECT', 'INTERFACE'); | ||
// Do we want to perpetuate this? (Obviously, this is for historical reason and some graphQL implementations still do | ||
// not support 'repeatable'. But since this code does not kick in within users' code, not sure we have to accomodate | ||
// not support 'repeatable'. But since this code does not kick in within users' code, not sure we have to accommodate | ||
// for those implementations. Besides, we _do_ accept if people re-defined @key as non-repeatable). | ||
@@ -324,3 +339,3 @@ keyDirective.repeatable = true; | ||
const queryType = queryRoot ? queryRoot.type : schema.addType(new ObjectType("Query")); | ||
let entityField = queryType.field(entitiesFieldName); | ||
const entityField = queryType.field(entitiesFieldName); | ||
if (hasEntities) { | ||
@@ -390,3 +405,3 @@ const anyType = schema.type(anyTypeName); | ||
// because if it's provided by the current subgraph, why "requires" it? That said, it's not 100% | ||
// non-sensical if you wanted a local field to be part of the subgraph fetch even if it's not | ||
// nonsensical if you wanted a local field to be part of the subgraph fetch even if it's not | ||
// truly queried _for some reason_. But it's unclear such reasons exists, so for now we prefer | ||
@@ -398,3 +413,3 @@ // rejecting it as it also make it less likely user misunderstand what @requires actually do. | ||
this.requiresDirective(schema), | ||
field => field.parent!, | ||
field => field.parent, | ||
field => `field "${field.coordinate}"`, | ||
@@ -409,11 +424,14 @@ errors, | ||
// external in a @provides (we pass `false` for the `allowOnNonExternalLeafFields` parameter), | ||
// but contrarily to @requires, there is probaly no reason to ever change this, as a @provides | ||
// of a field already provides is 100% non-sensical. | ||
// but contrarily to @requires, there is probably no reason to ever change this, as a @provides | ||
// of a field already provides is 100% nonsensical. | ||
validateAllFieldSet<FieldDefinition<CompositeType>>( | ||
this.providesDirective(schema), | ||
field => { | ||
if (externalTester.isExternal(field)) { | ||
throw new GraphQLError(`Cannot have both @provides and @external on field "${field.coordinate}"`, field.sourceAST); | ||
} | ||
const type = baseType(field.type!); | ||
if (!isObjectType(type)) { | ||
if (!isCompositeType(type)) { | ||
throw new GraphQLError( | ||
`Invalid @provides directive on field "${field.coordinate}": field has type "${field.type}" which is not an Object Type`, | ||
`Invalid @provides directive on field "${field.coordinate}": field has type "${field.type}" which is not a Composite Type`, | ||
field.sourceAST); | ||
@@ -453,7 +471,7 @@ } | ||
extendsDirective(schema: Schema): DirectiveDefinition<{}> { | ||
extendsDirective(schema: Schema): DirectiveDefinition<Record<string, never>> { | ||
return this.getTypedDirective(schema, extendsDirectiveName); | ||
} | ||
externalDirective(schema: Schema): DirectiveDefinition<{}> { | ||
externalDirective(schema: Schema): DirectiveDefinition<Record<string, never>> { | ||
return this.getTypedDirective(schema, externalDirectiveName); | ||
@@ -477,3 +495,3 @@ } | ||
let definitions = document.definitions.concat(); | ||
const definitions = document.definitions.concat(); | ||
for (const directiveName of FEDERATION_DIRECTIVES) { | ||
@@ -513,3 +531,3 @@ const directive = schema.directive(directiveName); | ||
export function isFederationField(field: FieldDefinition<CompositeType>): boolean { | ||
if (field.parent === field.schema()!.schemaDefinition.root("query")?.type) { | ||
if (field.parent === field.schema().schemaDefinition.root("query")?.type) { | ||
return FEDERATION_ROOT_FIELDS.includes(field.name); | ||
@@ -554,3 +572,3 @@ } | ||
throw new GraphQLError( | ||
`Invalid value for argument ${directive.definition!.argument('fields')!.coordinate} on ${directive.parent!.coordinate}: must be a string.`, | ||
`Invalid value for argument ${directive.definition!.argument('fields')!.coordinate} on ${directive.parent.coordinate}: must be a string.`, | ||
directive.sourceAST | ||
@@ -562,4 +580,2 @@ ); | ||
// 'ServiceDefinition' is originally defined in federation-js and we don't want to create a dependency | ||
// of internals-js to that just for that interface. | ||
export interface ServiceDefinition { | ||
@@ -589,13 +605,8 @@ typeDefs: DocumentNode; | ||
// Simple wrapper around a Subraph[] that ensures that 1) we never mistakenly get 2 subgraph with the same name, | ||
// Simple wrapper around a Subgraph[] that ensures that 1) we never mistakenly get 2 subgraph with the same name, | ||
// 2) keep the subgraphs sorted by name (makes iteration more predictable). It also allow convenient access to | ||
// a subgraph by name so behave like a map<string, Subgraph> in most ways (but with the previously mentioned benefits). | ||
export class Subgraphs { | ||
private readonly subgraphs: Subgraph[] = []; | ||
private readonly subgraphs = new OrderedMap<string, Subgraph>(); | ||
private idx(name: string): number { | ||
// Note: we could do a binary search if we ever worry that a linear scan is too costly. | ||
return this.subgraphs.findIndex(s => s.name === name); | ||
} | ||
add(subgraph: Subgraph): Subgraph; | ||
@@ -612,8 +623,6 @@ add(name: string, url: string, schema: Schema | DocumentNode | string): Subgraph; | ||
const idx = this.idx(toAdd.name); | ||
if (idx >= 0) { | ||
if (this.subgraphs.has(toAdd.name)) { | ||
throw new Error(`A subgraph named ${toAdd.name} already exists` + (toAdd.url ? ` (with url '${toAdd.url}')` : '')); | ||
} | ||
this.subgraphs.push(toAdd); | ||
this.subgraphs.sort(); | ||
this.subgraphs.add(toAdd.name, toAdd); | ||
return toAdd; | ||
@@ -623,28 +632,25 @@ } | ||
get(name: string): Subgraph | undefined { | ||
const idx = this.idx(name); | ||
return idx >= 0 ? this.subgraphs[idx] : undefined; | ||
return this.subgraphs.get(name); | ||
} | ||
getByIdx(idx: number): Subgraph { | ||
return this.subgraphs[idx]; | ||
} | ||
size(): number { | ||
return this.subgraphs.length; | ||
return this.subgraphs.size; | ||
} | ||
names(): readonly string[] { | ||
return this.subgraphs.map(s => s.name); | ||
return this.subgraphs.keys(); | ||
} | ||
values(): readonly Subgraph[] { | ||
return this.subgraphs; | ||
return this.subgraphs.values(); | ||
} | ||
[Symbol.iterator]() { | ||
return this.subgraphs.values(); | ||
*[Symbol.iterator]() { | ||
for (const subgraph of this.subgraphs) { | ||
yield subgraph; | ||
} | ||
} | ||
toString(): string { | ||
return '[' + this.subgraphs.map(s => s.name).join(', ') + ']' | ||
return '[' + this.subgraphs.keys().join(', ') + ']' | ||
} | ||
@@ -655,3 +661,3 @@ } | ||
constructor( | ||
readonly name: string, | ||
readonly name: string, | ||
readonly url: string, | ||
@@ -707,3 +713,3 @@ readonly schema: Schema, | ||
for (const key of keyDirective.applications()) { | ||
const parent = key.parent! as CompositeType; | ||
const parent = key.parent as CompositeType; | ||
if (!(key.ofExtension() || parent.hasAppliedDirective(extendsDirectiveName))) { | ||
@@ -710,0 +716,0 @@ continue; |
@@ -20,6 +20,6 @@ import { FeatureDefinition, FeatureDefinitions, FeatureUrl, FeatureVersion } from "./coreSpec"; | ||
addElementsToSchema(schema: Schema) { | ||
this.addDirective(schema, 'inacessible').addLocations('FIELD_DEFINITION', 'OBJECT', 'INTERFACE', 'UNION'); | ||
this.addDirective(schema, 'inaccessible').addLocations('FIELD_DEFINITION', 'OBJECT', 'INTERFACE', 'UNION'); | ||
} | ||
inaccessibleDirective(schema: Schema): DirectiveDefinition<{}> { | ||
inaccessibleDirective(schema: Schema): DirectiveDefinition<Record<string, never>> { | ||
return this.directive(schema, 'inaccessible')!; | ||
@@ -38,10 +38,10 @@ } | ||
const inacessibleFeature = coreFeatures.getByIdentity(inaccessibleIdentity); | ||
if (!inacessibleFeature) { | ||
const inaccessibleFeature = coreFeatures.getByIdentity(inaccessibleIdentity); | ||
if (!inaccessibleFeature) { | ||
return; | ||
} | ||
const inaccessibleSpec = INACCESSIBLE_VERSIONS.find(inacessibleFeature.url.version); | ||
const inaccessibleSpec = INACCESSIBLE_VERSIONS.find(inaccessibleFeature.url.version); | ||
if (!inaccessibleSpec) { | ||
throw new GraphQLError( | ||
`Cannot remove inacessible elements: the schema uses unsupported inacessible spec version ${inacessibleFeature.url.version} (supported versions: ${INACCESSIBLE_VERSIONS.versions().join(', ')})`); | ||
`Cannot remove inaccessible elements: the schema uses unsupported inaccessible spec version ${inaccessibleFeature.url.version} (supported versions: ${INACCESSIBLE_VERSIONS.versions().join(', ')})`); | ||
} | ||
@@ -57,3 +57,3 @@ | ||
for (const type of schema.types()) { | ||
// @inacessible can only be on composite types. | ||
// @inaccessible can only be on composite types. | ||
if (!isCompositeType(type)) { | ||
@@ -60,0 +60,0 @@ continue; |
@@ -14,5 +14,2 @@ import { FeatureDefinition, FeatureDefinitions, FeatureUrl, FeatureVersion } from "./coreSpec"; | ||
// Copied from federation-js/joinSpec.ts. Not reusing that existing version for now to | ||
// avoid creating a dependency on federation-js. But there is likely some re-org of | ||
// modules to do. | ||
function sanitizeGraphQLName(name: string) { | ||
@@ -19,0 +16,0 @@ // replace all non-word characters (\W). Word chars are _a-zA-Z0-9 |
@@ -89,3 +89,3 @@ import { | ||
) { | ||
super(definition.schema()!, variablesInArguments(args)); | ||
super(definition.schema(), variablesInArguments(args)); | ||
this.validate(); | ||
@@ -103,3 +103,3 @@ } | ||
get parentType(): CompositeType { | ||
return this.definition.parent!; | ||
return this.definition.parent; | ||
} | ||
@@ -127,3 +127,3 @@ | ||
// This code largely mirrors validate, so we could generalize that and return false on exception, but this | ||
// method is called fairly often and that has been shown to impact peformance quite a lot. So a little | ||
// method is called fairly often and that has been shown to impact performance quite a lot. So a little | ||
// bit of code duplication is ok. | ||
@@ -186,3 +186,3 @@ if (this.name !== definition.name) { | ||
const selectionParent = selectionSet.parentType; | ||
const fieldParent = this.definition.parent!; | ||
const fieldParent = this.definition.parent; | ||
if (selectionParent.name !== fieldParent.name) { | ||
@@ -226,4 +226,4 @@ if (this.name === typenameFieldName) { | ||
const entries = Object.entries(this.args); | ||
const args = entries.length == 0 | ||
? '' | ||
const args = entries.length == 0 | ||
? '' | ||
: '(' + entries.map(([n, v]) => `${n}: ${valueToString(v, this.definition.argument(n)?.type)}`).join(', ') + ')'; | ||
@@ -244,3 +244,3 @@ return alias + this.name + args + this.appliedDirectivesToString(); | ||
// the source type and the type condition) | ||
super(sourceType.schema()!, []); | ||
super(sourceType.schema(), []); | ||
this.typeCondition = typeCondition !== undefined && typeof typeCondition === 'string' | ||
@@ -377,3 +377,3 @@ ? this.schema().type(typeCondition)! as CompositeType | ||
} | ||
const schema = element.schema()!; | ||
const schema = element.schema(); | ||
for (const node of directiveNodes) { | ||
@@ -579,3 +579,3 @@ const directiveDef = schema.directive(node.name.value); | ||
// If any of the existing fragments of the selection set is also a name in the provided one, | ||
// we bail out of optimizing anyting. Not ideal, but dealing with it properly complicate things | ||
// we bail out of optimizing anything. Not ideal, but dealing with it properly complicate things | ||
// and we probably don't care for now as we'll call `optimize` mainly on result sets that have | ||
@@ -627,3 +627,3 @@ // no named fragments in the first place. | ||
const key = toAdd.key(); | ||
let existing: Selection[] | undefined = this._selections.get(key); | ||
const existing: Selection[] | undefined = this._selections.get(key); | ||
if (existing) { | ||
@@ -772,3 +772,3 @@ for (const existingSelection of existing) { | ||
// query to stop where the validation problem lies, even if we're not on a leaf type. To make this look nice and | ||
// explicit, we handle that case by create a fake selection set that just contains an elipsis, indicate there is | ||
// explicit, we handle that case by create a fake selection set that just contains an ellipsis, indicate there is | ||
// supposed to be more but we elided it for clarity. And yes, the whole thing is a bit of a hack, albeit a convenient | ||
@@ -797,3 +797,3 @@ // one. | ||
// If __typename is selected however, we put it first. It's a detail but as __typename is a bit special it looks better, | ||
// and it happens to mimick prior behavior on the query plan side so it saves us from changing tests for no good reasons. | ||
// and it happens to mimic prior behavior on the query plan side so it saves us from changing tests for no good reasons. | ||
const typenameSelection = this._selections.get(typenameFieldName); | ||
@@ -849,3 +849,3 @@ if (typenameSelection) { | ||
/** | ||
* The string respresentation of this selection set. | ||
* The string representation of this selection set. | ||
* | ||
@@ -855,3 +855,3 @@ * By default, this expand all fragments so that the returned string is self-contained. You can | ||
* definitions will _not_ be included in the returned string. If you want a representation of | ||
* this selection set with fragements definitions included, use `toOperationString` instead. | ||
* this selection set with fragments definitions included, use `toOperationString` instead. | ||
*/ | ||
@@ -910,3 +910,3 @@ toString( | ||
const type = baseType(field.definition.type!); | ||
// Field types are output type, and a named typethat is an output one and isn't a leat is guaranteed to be selectable. | ||
// Field types are output type, and a named typethat is an output one and isn't a leaf is guaranteed to be selectable. | ||
this.selectionSet = isLeafType(type) ? undefined : (initialSelectionSet ? initialSelectionSet : new SelectionSet(type as CompositeType)); | ||
@@ -963,3 +963,3 @@ } | ||
validate() { | ||
// Note that validation is kind of redudant since `this.selectionSet.validate()` will check that it isn't empty. But doing it | ||
// Note that validation is kind of redundant since `this.selectionSet.validate()` will check that it isn't empty. But doing it | ||
// allow to provide much better error messages. | ||
@@ -1105,3 +1105,3 @@ validate( | ||
validate() { | ||
// Note that validation is kind of redudant since `this.selectionSet.validate()` will check that it isn't empty. But doing it | ||
// Note that validation is kind of redundant since `this.selectionSet.validate()` will check that it isn't empty. But doing it | ||
// allow to provide much better error messages. | ||
@@ -1181,3 +1181,3 @@ validate( | ||
private readonly namedFragment: NamedFragmentDefinition; | ||
// Note that the named fragement directives are copied on this element and appear first (the spreadDirectives | ||
// Note that the named fragment directives are copied on this element and appear first (the spreadDirectives | ||
// method rely on this to be able to extract the directives that are specific to the spread itself). | ||
@@ -1310,3 +1310,3 @@ private readonly _element : FragmentElement; | ||
// Note that we need the variables to handle the fragments, as they can be used there. | ||
const variableDefinitions = operation.variableDefinitions | ||
const variableDefinitions = operation.variableDefinitions | ||
? variableDefinitionsFromAST(schema, operation.variableDefinitions) | ||
@@ -1355,3 +1355,3 @@ : new VariableDefinitions(); | ||
): SelectionSet { | ||
// TODO: we sould maybe allow the selection, when a string, to contain fragment definitions? | ||
// TODO: we should maybe allow the selection, when a string, to contain fragment definitions? | ||
const node = typeof source === 'string' | ||
@@ -1358,0 +1358,0 @@ ? parseOperationAST(source.trim().startsWith('{') ? source : `{${source}}`).selectionSet |
@@ -35,3 +35,3 @@ import { | ||
showNonGraphQLBuiltIns: boolean; | ||
noDesciptions: boolean; | ||
noDescriptions: boolean; | ||
} | ||
@@ -46,3 +46,3 @@ | ||
showNonGraphQLBuiltIns: false, | ||
noDesciptions: false, | ||
noDescriptions: false, | ||
} | ||
@@ -205,3 +205,3 @@ | ||
): string { | ||
if (element.description === undefined || options.noDesciptions) { | ||
if (element.description === undefined || options.noDescriptions) { | ||
return ''; | ||
@@ -268,3 +268,3 @@ } | ||
} | ||
const vals = values.map((v, i) => | ||
const vals = values.map((v, i) => | ||
printDescription(v, options, options.indentString, !i) | ||
@@ -299,4 +299,4 @@ + options.indentString | ||
printDescription(f, options, options.indentString, !i) | ||
+ options.indentString | ||
+ printField(f, options) | ||
+ options.indentString | ||
+ printField(f, options) | ||
+ printAppliedDirectives(f.appliedDirectives, options))); | ||
@@ -306,4 +306,4 @@ } | ||
function printField(field: FieldDefinition<any> | InputFieldDefinition, options: Options): string { | ||
let args = field.kind == 'FieldDefinition' ? printArgs(field.arguments(), options, options.indentString) : ''; | ||
let defaultValue = field.kind === 'InputFieldDefinition' && field.defaultValue !== undefined | ||
const args = field.kind == 'FieldDefinition' ? printArgs(field.arguments(), options, options.indentString) : ''; | ||
const defaultValue = field.kind === 'InputFieldDefinition' && field.defaultValue !== undefined | ||
? ' = ' + valueToString(field.defaultValue, field.type) | ||
@@ -310,0 +310,0 @@ : ''; |
@@ -36,3 +36,3 @@ import levenshtein from 'js-levenshtein'; | ||
export function didYouMean(suggestions: readonly string[]): string { | ||
let message = ' Did you mean '; | ||
const message = ' Did you mean '; | ||
@@ -39,0 +39,0 @@ const quotedSuggestions = suggestions.map((x) => `"${x}"`); |
@@ -32,3 +32,3 @@ import { DirectiveLocationEnum, GraphQLError } from "graphql"; | ||
const nameArg = definition.argument('name'); | ||
const hasValidNameArg = nameArg && sameType(nameArg.type!, new NonNullType(definition.schema()!.stringType())); | ||
const hasValidNameArg = nameArg && sameType(nameArg.type!, new NonNullType(definition.schema().stringType())); | ||
const hasValidLocations = definition.locations.every(loc => tagLocations.includes(loc)); | ||
@@ -35,0 +35,0 @@ if (hasUnknownArguments || !hasValidNameArg || !hasValidLocations) { |
@@ -114,3 +114,3 @@ /** | ||
* Strict subtyping is the subtyping relation defined on `isSubtype`, but where | ||
* equality (as definied by `sameType`) is excluded. | ||
* equality (as defined by `sameType`) is excluded. | ||
*/ | ||
@@ -117,0 +117,0 @@ export function isStrictSubtype( |
@@ -33,2 +33,84 @@ /** | ||
/** | ||
* Generic OrderedMap class that can sort keys based on an arbitrary sorting function | ||
* Insert time is O(log(N)) | ||
* Remove is not implemented, but the trivial implementation would be O(N) | ||
* Uses '<' '>' sorting by default | ||
* Collisions are fine, it will just overwrite the old value | ||
*/ | ||
export class OrderedMap<K,V> { | ||
private _keys: K[] = []; | ||
private _values: Map<K,V> = new Map<K,V>(); | ||
private _compareFn: (a: K, b: K) => number; | ||
private static defaultCompareFn<K>(a: K, b: K) { | ||
if (a < b) { | ||
return -1; | ||
} else if (b < a) { | ||
return 1; | ||
} | ||
return 0; | ||
} | ||
constructor(compareFn: (a: K, b: K) => number = OrderedMap.defaultCompareFn) { | ||
this._compareFn = compareFn; | ||
} | ||
add(key: K, value: V) { | ||
if (!this._values.has(key)) { | ||
this.insertKeyInOrder(key); | ||
} | ||
this._values.set(key, value); | ||
} | ||
get(key: K): V | undefined { | ||
return this._values.get(key); | ||
} | ||
has(key: K): boolean { | ||
return this._values.has(key); | ||
} | ||
get size() { | ||
return this._keys.length; | ||
} | ||
keys(): K[] { | ||
return this._keys; | ||
} | ||
values(): V[] { | ||
return this._keys.map(key => { | ||
const v = this._values.get(key); | ||
assert(v, 'value for known key not found in OrderedMap'); | ||
return v; | ||
}); | ||
} | ||
// O(log(N)) - find location via middle finding | ||
private insertKeyInOrder(key: K) { | ||
let lower = 0; | ||
let upper = this._keys.length - 1; | ||
while (lower <= upper) { | ||
const middle = Math.floor((upper + lower) / 2); | ||
if (this._compareFn(this._keys[middle], key) < 0) { | ||
lower = middle + 1; | ||
} else { | ||
upper = middle - 1; | ||
} | ||
} | ||
this._keys = this._keys.slice(0, lower).concat(key).concat(this._keys.slice(lower)); | ||
} | ||
// remove(key: K): void - not implemented | ||
*[Symbol.iterator]() { | ||
for (let i = 0; i < this._keys.length; i += 1) { | ||
const v = this._values.get(this._keys[i]); | ||
assert(v, 'value for known key not found in OrderedMap'); | ||
yield v; | ||
} | ||
} | ||
} | ||
/** | ||
* Tests if the provided arrays have the same elements (using '===' equality or the provided | ||
@@ -35,0 +117,0 @@ * equality function). |
@@ -24,3 +24,3 @@ import { | ||
// Note really meant to be called manually as it is part of `Schema.validate`, but separated for core-organisation reasons. | ||
// Note really meant to be called manually as it is part of `Schema.validate`, but separated for core-organization reasons. | ||
// This mostly apply the validations that graphQL-js does in `validateSchema` which we don't reuse because it applies to | ||
@@ -179,3 +179,3 @@ // a `GraphQLSchema` (but note that the bulk of the validation is done by `validateSDL` which we _do_ reuse in `Schema.validate`). | ||
this.errors.push(new GraphQLError( | ||
`Interface field ${itfField.coordinate} expected but ${type} does not provide it.`, | ||
`Interface field ${itfField.coordinate} expected but ${type} does not provide it.`, | ||
sourceASTs(itfField, type) | ||
@@ -207,3 +207,3 @@ )); | ||
this.validateHasType(itfArg); | ||
// Note that we could use contra-variance but as graphQL-js currently doesn't allow it, we mimick that. | ||
// Note that we could use contra-variance but as graphQL-js currently doesn't allow it, we mimic that. | ||
if (!sameType(itfArg.type!, arg.type!)) { | ||
@@ -306,3 +306,3 @@ this.errors.push(new GraphQLError( | ||
if (!isValidValue(value, argument, this.emptyVariables)) { | ||
const parent = application.parent!; | ||
const parent = application.parent; | ||
// The only non-named SchemaElement is the `schema` definition. | ||
@@ -309,0 +309,0 @@ const parentDesc = parent instanceof NamedSchemaElement |
@@ -69,3 +69,3 @@ import { | ||
return '{' + Object.keys(v).map(k => { | ||
let valueType = expectedType ? (expectedType as InputObjectType).field(k)?.type : undefined; | ||
const valueType = expectedType ? (expectedType as InputObjectType).field(k)?.type : undefined; | ||
return `${k}: ${valueToString(v[k], valueType)}`; | ||
@@ -80,3 +80,3 @@ }).join(', ') + '}'; | ||
} | ||
if (expectedType === expectedType.schema()!.idType() && integerStringRegExp.test(v)) { | ||
if (expectedType === expectedType.schema().idType() && integerStringRegExp.test(v)) { | ||
return v; | ||
@@ -313,3 +313,3 @@ } | ||
// ID types can use Int literals. | ||
if (type === type.schema()?.idType() && integerStringRegExp.test(value)) { | ||
if (type === type.schema().idType() && integerStringRegExp.test(value)) { | ||
return { kind: Kind.INT, value: value }; | ||
@@ -465,3 +465,3 @@ } | ||
// though). | ||
const schema = locationType.schema()!; | ||
const schema = locationType.schema(); | ||
@@ -468,0 +468,0 @@ if (typeof value === 'boolean') { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1089617
4
128
17259
+ Added@apollo/core-schema@0.2.3(transitive)
- Removed@types/jest@^26.0.23
- Removed@apollo/core-schema@0.1.1(transitive)
- Removed@jest/types@26.6.2(transitive)
- Removed@types/istanbul-lib-coverage@2.0.6(transitive)
- Removed@types/istanbul-lib-report@3.0.3(transitive)
- Removed@types/istanbul-reports@3.0.4(transitive)
- Removed@types/jest@26.0.24(transitive)
- Removed@types/node@22.8.6(transitive)
- Removed@types/yargs@15.0.19(transitive)
- Removed@types/yargs-parser@21.0.3(transitive)
- Removedansi-regex@5.0.1(transitive)
- Removeddiff-sequences@26.6.2(transitive)
- Removedjest-diff@26.6.2(transitive)
- Removedjest-get-type@26.3.0(transitive)
- Removedpretty-format@26.6.2(transitive)
- Removedreact-is@17.0.2(transitive)
- Removedundici-types@6.19.8(transitive)
Updated@apollo/core-schema@^0.2.0