diff --git a/src/index.ts b/src/index.ts index c09efb0456..d3641deb73 100644 --- a/src/index.ts +++ b/src/index.ts @@ -126,6 +126,9 @@ export { /** Validate GraphQL schema. */ validateSchema, assertValidSchema, + /** Upholds the spec rules about naming. */ + assertName, + assertEnumValueName, } from './type/index'; export type { diff --git a/src/type/__tests__/assertName-test.ts b/src/type/__tests__/assertName-test.ts new file mode 100644 index 0000000000..5bc1a12b83 --- /dev/null +++ b/src/type/__tests__/assertName-test.ts @@ -0,0 +1,71 @@ +import { expect } from 'chai'; +import { describe, it } from 'mocha'; + +import { assertName, assertEnumValueName } from '../assertName'; +import { assertValidName } from '../../utilities/assertValidName'; + +describe('assertValidName()', () => { + assertValidName('test'); + it('passthrough valid name', () => { + expect(assertName('_ValidName123')).to.equal('_ValidName123'); + }); + + it('throws for non-strings', () => { + // @ts-expect-error + expect(() => assertName({})).to.throw('Expected name to be a string.'); + }); + + it('throws on empty strings', () => { + expect(() => assertName('')).to.throw( + 'Expected name to be a non-empty string.', + ); + }); + + it('throws for names with invalid characters', () => { + expect(() => assertName('>--()-->')).to.throw( + 'Names must only contain [_a-zA-Z0-9] but ">--()-->" does not.', + ); + }); + + it('throws for names starting with invalid characters', () => { + expect(() => assertName('42MeaningsOfLife')).to.throw( + 'Names must start with [_a-zA-Z] but "42MeaningsOfLife" does not.', + ); + }); +}); + +describe('assertEnumValueName()', () => { + it('passthrough valid name', () => { + expect(assertEnumValueName('_ValidName123')).to.equal('_ValidName123'); + }); + + it('throws on empty strings', () => { + expect(() => assertEnumValueName('')).to.throw( + 'Expected name to be a non-empty string.', + ); + }); + + it('throws for names with invalid characters', () => { + expect(() => assertEnumValueName('>--()-->')).to.throw( + 'Names must only contain [_a-zA-Z0-9] but ">--()-->" does not.', + ); + }); + + it('throws for names starting with invalid characters', () => { + expect(() => assertEnumValueName('42MeaningsOfLife')).to.throw( + 'Names must start with [_a-zA-Z] but "42MeaningsOfLife" does not.', + ); + }); + + it('throws for restricted names', () => { + expect(() => assertEnumValueName('true')).to.throw( + 'Enum values cannot be named: true', + ); + expect(() => assertEnumValueName('false')).to.throw( + 'Enum values cannot be named: false', + ); + expect(() => assertEnumValueName('null')).to.throw( + 'Enum values cannot be named: null', + ); + }); +}); diff --git a/src/type/__tests__/definition-test.ts b/src/type/__tests__/definition-test.ts index 0b73a02173..9de80c072f 100644 --- a/src/type/__tests__/definition-test.ts +++ b/src/type/__tests__/definition-test.ts @@ -324,9 +324,10 @@ describe('Type System: Objects', () => { expect(() => objType.getFields()).to.not.throw(); }); - it('rejects an Object type without name', () => { - // @ts-expect-error - expect(() => new GraphQLObjectType({})).to.throw('Must provide name.'); + it('rejects an Object type with invalid name', () => { + expect( + () => new GraphQLObjectType({ name: 'bad-name', fields: {} }), + ).to.throw('Names must only contain [_a-zA-Z0-9] but "bad-name" does not.'); }); it('rejects an Object type field with undefined config', () => { @@ -353,6 +354,18 @@ describe('Type System: Objects', () => { ); }); + it('rejects an Object type with incorrectly named fields', () => { + const objType = new GraphQLObjectType({ + name: 'SomeObject', + fields: { + 'bad-name': { type: ScalarType }, + }, + }); + expect(() => objType.getFields()).to.throw( + 'Names must only contain [_a-zA-Z0-9] but "bad-name" does not.', + ); + }); + it('rejects an Object type with a field function that returns incorrect type', () => { const objType = new GraphQLObjectType({ name: 'SomeObject', @@ -380,6 +393,23 @@ describe('Type System: Objects', () => { ); }); + it('rejects an Object type with incorrectly named field args', () => { + const objType = new GraphQLObjectType({ + name: 'SomeObject', + fields: { + badField: { + type: ScalarType, + args: { + 'bad-name': { type: ScalarType }, + }, + }, + }, + }); + expect(() => objType.getFields()).to.throw( + 'Names must only contain [_a-zA-Z0-9] but "bad-name" does not.', + ); + }); + it('rejects an Object type with incorrectly typed interfaces', () => { const objType = new GraphQLObjectType({ name: 'SomeObject', @@ -478,9 +508,10 @@ describe('Type System: Interfaces', () => { expect(implementing.getInterfaces()).to.deep.equal([InterfaceType]); }); - it('rejects an Interface type without name', () => { - // @ts-expect-error - expect(() => new GraphQLInterfaceType({})).to.throw('Must provide name.'); + it('rejects an Interface type with invalid name', () => { + expect( + () => new GraphQLInterfaceType({ name: 'bad-name', fields: {} }), + ).to.throw('Names must only contain [_a-zA-Z0-9] but "bad-name" does not.'); }); it('rejects an Interface type with incorrectly typed interfaces', () => { @@ -559,9 +590,10 @@ describe('Type System: Unions', () => { expect(unionType.getTypes()).to.deep.equal([]); }); - it('rejects an Union type without name', () => { - // @ts-expect-error - expect(() => new GraphQLUnionType({})).to.throw('Must provide name.'); + it('rejects an Union type with invalid name', () => { + expect( + () => new GraphQLUnionType({ name: 'bad-name', types: [] }), + ).to.throw('Names must only contain [_a-zA-Z0-9] but "bad-name" does not.'); }); it('rejects an Union type with an incorrect type for resolveType', () => { @@ -674,11 +706,10 @@ describe('Type System: Enums', () => { expect(enumType.getValue('BAR')).has.property('value', 20); }); - it('rejects an Enum type without name', () => { - // @ts-expect-error - expect(() => new GraphQLEnumType({ values: {} })).to.throw( - 'Must provide name.', - ); + it('rejects an Enum type with invalid name', () => { + expect( + () => new GraphQLEnumType({ name: 'bad-name', values: {} }), + ).to.throw('Names must only contain [_a-zA-Z0-9] but "bad-name" does not.'); }); it('rejects an Enum type with incorrectly typed values', () => { @@ -692,6 +723,18 @@ describe('Type System: Enums', () => { ).to.throw('SomeEnum values must be an object with value names as keys.'); }); + it('rejects an Enum type with incorrectly named values', () => { + expect( + () => + new GraphQLEnumType({ + name: 'SomeEnum', + values: { + 'bad-name': {}, + }, + }), + ).to.throw('Names must only contain [_a-zA-Z0-9] but "bad-name" does not.'); + }); + it('rejects an Enum type with missing value definition', () => { expect( () => @@ -761,10 +804,11 @@ describe('Type System: Input Objects', () => { }); }); - it('rejects an Input Object type without name', () => { - // @ts-expect-error - expect(() => new GraphQLInputObjectType({})).to.throw( - 'Must provide name.', + it('rejects an Input Object type with invalid name', () => { + expect( + () => new GraphQLInputObjectType({ name: 'bad-name', fields: {} }), + ).to.throw( + 'Names must only contain [_a-zA-Z0-9] but "bad-name" does not.', ); }); @@ -789,6 +833,18 @@ describe('Type System: Input Objects', () => { 'SomeInputObject fields must be an object with field names as keys or a function which returns such an object.', ); }); + + it('rejects an Input Object type with incorrectly named fields', () => { + const inputObjType = new GraphQLInputObjectType({ + name: 'SomeInputObject', + fields: { + 'bad-name': { type: ScalarType }, + }, + }); + expect(() => inputObjType.getFields()).to.throw( + 'Names must only contain [_a-zA-Z0-9] but "bad-name" does not.', + ); + }); }); describe('Input Object fields must not have resolvers', () => { diff --git a/src/type/__tests__/directive-test.ts b/src/type/__tests__/directive-test.ts index 3b481bd10f..9496d57ddd 100644 --- a/src/type/__tests__/directive-test.ts +++ b/src/type/__tests__/directive-test.ts @@ -84,11 +84,10 @@ describe('Type System: Directive', () => { ); }); - it('rejects an unnamed directive', () => { - // @ts-expect-error - expect(() => new GraphQLDirective({ locations: ['QUERY'] })).to.throw( - 'Directive must be named.', - ); + it('rejects a directive with invalid name', () => { + expect( + () => new GraphQLDirective({ name: 'bad-name', locations: ['QUERY'] }), + ).to.throw('Names must only contain [_a-zA-Z0-9] but "bad-name" does not.'); }); it('rejects a directive with incorrectly typed args', () => { @@ -103,6 +102,19 @@ describe('Type System: Directive', () => { ).to.throw('@Foo args must be an object with argument names as keys.'); }); + it('rejects a directive with incorrectly named arg', () => { + expect( + () => + new GraphQLDirective({ + name: 'Foo', + locations: ['QUERY'], + args: { + 'bad-name': { type: GraphQLString }, + }, + }), + ).to.throw('Names must only contain [_a-zA-Z0-9] but "bad-name" does not.'); + }); + it('rejects a directive with undefined locations', () => { // @ts-expect-error expect(() => new GraphQLDirective({ name: 'Foo' })).to.throw( diff --git a/src/type/__tests__/validation-test.ts b/src/type/__tests__/validation-test.ts index 8325f69485..a39f3a4272 100644 --- a/src/type/__tests__/validation-test.ts +++ b/src/type/__tests__/validation-test.ts @@ -18,7 +18,6 @@ import type { GraphQLFieldConfig, GraphQLArgumentConfig, GraphQLInputFieldConfig, - GraphQLEnumValueConfigMap, } from '../definition'; import { GraphQLSchema } from '../schema'; import { GraphQLString } from '../scalars'; @@ -487,13 +486,15 @@ describe('Type System: Objects must have fields', () => { const schema = schemaWithFieldType( new GraphQLObjectType({ name: 'SomeObject', - fields: { 'bad-name-with-dashes': { type: GraphQLString } }, + fields: { + __badName: { type: GraphQLString }, + }, }), ); expectJSON(validateSchema(schema)).to.deep.equal([ { message: - 'Names must only contain [_a-zA-Z0-9] but "bad-name-with-dashes" does not.', + 'Name "__badName" must not begin with "__", which is reserved by GraphQL introspection.', }, ]); }); @@ -525,7 +526,7 @@ describe('Type System: Fields args must be properly named', () => { badField: { type: GraphQLString, args: { - 'bad-name-with-dashes': { type: GraphQLString }, + __badName: { type: GraphQLString }, }, }, }, @@ -535,7 +536,7 @@ describe('Type System: Fields args must be properly named', () => { expectJSON(validateSchema(schema)).to.deep.equal([ { message: - 'Names must only contain [_a-zA-Z0-9] but "bad-name-with-dashes" does not.', + 'Name "__badName" must not begin with "__", which is reserved by GraphQL introspection.', }, ]); }); @@ -956,51 +957,21 @@ describe('Type System: Enum types must be well defined', () => { }); it('rejects an Enum type with incorrectly named values', () => { - function schemaWithEnum(values: GraphQLEnumValueConfigMap): GraphQLSchema { - return schemaWithFieldType( - new GraphQLEnumType({ - name: 'SomeEnum', - values, - }), - ); - } - - const schema1 = schemaWithEnum({ '#value': {} }); - expectJSON(validateSchema(schema1)).to.deep.equal([ - { - message: 'Names must start with [_a-zA-Z] but "#value" does not.', - }, - ]); - - const schema2 = schemaWithEnum({ '1value': {} }); - expectJSON(validateSchema(schema2)).to.deep.equal([ - { - message: 'Names must start with [_a-zA-Z] but "1value" does not.', - }, - ]); + const schema = schemaWithFieldType( + new GraphQLEnumType({ + name: 'SomeEnum', + values: { + __badName: {}, + }, + }), + ); - const schema3 = schemaWithEnum({ 'KEBAB-CASE': {} }); - expectJSON(validateSchema(schema3)).to.deep.equal([ + expectJSON(validateSchema(schema)).to.deep.equal([ { message: - 'Names must only contain [_a-zA-Z0-9] but "KEBAB-CASE" does not.', + 'Name "__badName" must not begin with "__", which is reserved by GraphQL introspection.', }, ]); - - const schema4 = schemaWithEnum({ true: {} }); - expectJSON(validateSchema(schema4)).to.deep.equal([ - { message: 'Enum type SomeEnum cannot include value: true.' }, - ]); - - const schema5 = schemaWithEnum({ false: {} }); - expectJSON(validateSchema(schema5)).to.deep.equal([ - { message: 'Enum type SomeEnum cannot include value: false.' }, - ]); - - const schema6 = schemaWithEnum({ null: {} }); - expectJSON(validateSchema(schema6)).to.deep.equal([ - { message: 'Enum type SomeEnum cannot include value: null.' }, - ]); }); }); diff --git a/src/type/assertName.ts b/src/type/assertName.ts new file mode 100644 index 0000000000..c44361912b --- /dev/null +++ b/src/type/assertName.ts @@ -0,0 +1,44 @@ +import { devAssert } from '../jsutils/devAssert'; + +import { GraphQLError } from '../error/GraphQLError'; +import { isNameStart, isNameContinue } from '../language/characterClasses'; + +/** + * Upholds the spec rules about naming. + */ +export function assertName(name: string): string { + devAssert(name != null, 'Must provide name.'); + devAssert(typeof name === 'string', 'Expected name to be a string.'); + + if (name.length === 0) { + throw new GraphQLError('Expected name to be a non-empty string.'); + } + + for (let i = 1; i < name.length; ++i) { + if (!isNameContinue(name.charCodeAt(i))) { + throw new GraphQLError( + `Names must only contain [_a-zA-Z0-9] but "${name}" does not.`, + ); + } + } + + if (!isNameStart(name.charCodeAt(0))) { + throw new GraphQLError( + `Names must start with [_a-zA-Z] but "${name}" does not.`, + ); + } + + return name; +} + +/** + * Upholds the spec rules about naming enum values. + * + * @internal + */ +export function assertEnumValueName(name: string): string { + if (name === 'true' || name === 'false' || name === 'null') { + throw new GraphQLError(`Enum values cannot be named: ${name}`); + } + return assertName(name); +} diff --git a/src/type/definition.ts b/src/type/definition.ts index ab3dfcfc1b..11bc335ff8 100644 --- a/src/type/definition.ts +++ b/src/type/definition.ts @@ -43,6 +43,7 @@ import type { import { valueFromASTUntyped } from '../utilities/valueFromASTUntyped'; import type { GraphQLSchema } from './schema'; +import { assertName, assertEnumValueName } from './assertName'; // Predicates & Assertions @@ -595,7 +596,7 @@ export class GraphQLScalarType { config.parseValue ?? (identityFunc as GraphQLScalarValueParser); - this.name = config.name; + this.name = assertName(config.name); this.description = config.description; this.specifiedByURL = config.specifiedByURL; this.serialize = @@ -608,8 +609,6 @@ export class GraphQLScalarType { this.astNode = config.astNode; this.extensionASTNodes = config.extensionASTNodes ?? []; - devAssert(typeof config.name === 'string', 'Must provide name.'); - devAssert( config.specifiedByURL == null || typeof config.specifiedByURL === 'string', @@ -763,7 +762,7 @@ export class GraphQLObjectType { private _interfaces: ThunkReadonlyArray; constructor(config: Readonly>) { - this.name = config.name; + this.name = assertName(config.name); this.description = config.description; this.isTypeOf = config.isTypeOf; this.extensions = toObjMap(config.extensions); @@ -772,7 +771,6 @@ export class GraphQLObjectType { this._fields = () => defineFieldMap(config); this._interfaces = () => defineInterfaces(config); - devAssert(typeof config.name === 'string', 'Must provide name.'); devAssert( config.isTypeOf == null || typeof config.isTypeOf === 'function', `${this.name} must provide "isTypeOf" as a function, ` + @@ -863,7 +861,7 @@ function defineFieldMap( ); return { - name: fieldName, + name: assertName(fieldName), description: fieldConfig.description, type: fieldConfig.type, args: defineArguments(argsConfig), @@ -880,7 +878,7 @@ export function defineArguments( config: GraphQLFieldConfigArgumentMap, ): ReadonlyArray { return Object.entries(config).map(([argName, argConfig]) => ({ - name: argName, + name: assertName(argName), description: argConfig.description, type: argConfig.type, defaultValue: argConfig.defaultValue, @@ -1129,7 +1127,7 @@ export class GraphQLInterfaceType { private _interfaces: ThunkReadonlyArray; constructor(config: Readonly>) { - this.name = config.name; + this.name = assertName(config.name); this.description = config.description; this.resolveType = config.resolveType; this.extensions = toObjMap(config.extensions); @@ -1138,7 +1136,6 @@ export class GraphQLInterfaceType { this._fields = defineFieldMap.bind(undefined, config); this._interfaces = defineInterfaces.bind(undefined, config); - devAssert(typeof config.name === 'string', 'Must provide name.'); devAssert( config.resolveType == null || typeof config.resolveType === 'function', `${this.name} must provide "resolveType" as a function, ` + @@ -1258,7 +1255,7 @@ export class GraphQLUnionType { private _types: ThunkReadonlyArray; constructor(config: Readonly>) { - this.name = config.name; + this.name = assertName(config.name); this.description = config.description; this.resolveType = config.resolveType; this.extensions = toObjMap(config.extensions); @@ -1266,7 +1263,6 @@ export class GraphQLUnionType { this.extensionASTNodes = config.extensionASTNodes ?? []; this._types = defineTypes.bind(undefined, config); - devAssert(typeof config.name === 'string', 'Must provide name.'); devAssert( config.resolveType == null || typeof config.resolveType === 'function', `${this.name} must provide "resolveType" as a function, ` + @@ -1387,7 +1383,7 @@ export class GraphQLEnumType /* */ { private _nameLookup: ObjMap; constructor(config: Readonly */>) { - this.name = config.name; + this.name = assertName(config.name); this.description = config.description; this.extensions = toObjMap(config.extensions); this.astNode = config.astNode; @@ -1398,8 +1394,6 @@ export class GraphQLEnumType /* */ { this._values.map((enumValue) => [enumValue.value, enumValue]), ); this._nameLookup = keyMap(this._values, (value) => value.name); - - devAssert(typeof config.name === 'string', 'Must provide name.'); } getValues(): ReadonlyArray */> { @@ -1526,7 +1520,7 @@ function defineEnumValues( `representing an internal value but got: ${inspect(valueConfig)}.`, ); return { - name: valueName, + name: assertEnumValueName(valueName), description: valueConfig.description, value: valueConfig.value !== undefined ? valueConfig.value : valueName, deprecationReason: valueConfig.deprecationReason, @@ -1627,14 +1621,13 @@ export class GraphQLInputObjectType { private _fields: ThunkObjMap; constructor(config: Readonly) { - this.name = config.name; + this.name = assertName(config.name); this.description = config.description; this.extensions = toObjMap(config.extensions); this.astNode = config.astNode; this.extensionASTNodes = config.extensionASTNodes ?? []; this._fields = defineInputFieldMap.bind(undefined, config); - devAssert(typeof config.name === 'string', 'Must provide name.'); } getFields(): GraphQLInputFieldMap { @@ -1692,7 +1685,7 @@ function defineInputFieldMap( ); return { - name: fieldName, + name: assertName(fieldName), description: fieldConfig.description, type: fieldConfig.type, defaultValue: fieldConfig.defaultValue, diff --git a/src/type/directives.ts b/src/type/directives.ts index 0179c72961..5375746d0c 100644 --- a/src/type/directives.ts +++ b/src/type/directives.ts @@ -13,6 +13,7 @@ import type { GraphQLArgument, GraphQLFieldConfigArgumentMap, } from './definition'; +import { assertName } from './assertName'; import { GraphQLString, GraphQLBoolean } from './scalars'; import { defineArguments, @@ -63,14 +64,13 @@ export class GraphQLDirective { astNode: Maybe; constructor(config: Readonly) { - this.name = config.name; + this.name = assertName(config.name); this.description = config.description; this.locations = config.locations; this.isRepeatable = config.isRepeatable ?? false; this.extensions = toObjMap(config.extensions); this.astNode = config.astNode; - devAssert(config.name, 'Directive must be named.'); devAssert( Array.isArray(config.locations), `@${config.name} locations must be an Array.`, diff --git a/src/type/index.ts b/src/type/index.ts index 5686fd71f9..2561426b53 100644 --- a/src/type/index.ts +++ b/src/type/index.ts @@ -176,3 +176,6 @@ export { /** Validate GraphQL schema. */ export { validateSchema, assertValidSchema } from './validate'; + +/** Upholds the spec rules about naming. */ +export { assertName, assertEnumValueName } from './assertName'; diff --git a/src/type/validate.ts b/src/type/validate.ts index 77420d8bc5..b949365a5a 100644 --- a/src/type/validate.ts +++ b/src/type/validate.ts @@ -2,7 +2,6 @@ import { inspect } from '../jsutils/inspect'; import type { Maybe } from '../jsutils/Maybe'; import { GraphQLError } from '../error/GraphQLError'; -import { locatedError } from '../error/locatedError'; import type { ASTNode, @@ -17,7 +16,6 @@ import type { UnionTypeExtensionNode, } from '../language/ast'; -import { isValidNameError } from '../utilities/assertValidName'; import { isEqualType, isTypeSubTypeOf } from '../utilities/typeComparators'; import type { GraphQLSchema } from './schema'; @@ -104,11 +102,7 @@ class SchemaValidationContext { const _nodes = Array.isArray(nodes) ? (nodes.filter(Boolean) as ReadonlyArray) : (nodes as Maybe); - this.addError(new GraphQLError(message, _nodes)); - } - - addError(error: GraphQLError): void { - this._errors.push(error); + this._errors.push(new GraphQLError(message, _nodes)); } getErrors(): ReadonlyArray { @@ -205,9 +199,11 @@ function validateName( node: { readonly name: string; readonly astNode: Maybe }, ): void { // Ensure names are valid, however introspection types opt out. - const error = isValidNameError(node.name); - if (error) { - context.addError(locatedError(error, node.astNode)); + if (node.name.startsWith('__')) { + context.reportError( + `Name "${node.name}" must not begin with "__", which is reserved by GraphQL introspection.`, + node.astNode, + ); } } @@ -498,16 +494,8 @@ function validateEnumValues( } for (const enumValue of enumValues) { - const valueName = enumValue.name; - // Ensure valid name. validateName(context, enumValue); - if (valueName === 'true' || valueName === 'false' || valueName === 'null') { - context.reportError( - `Enum type ${enumType.name} cannot include value: ${valueName}.`, - enumValue.astNode, - ); - } } } diff --git a/src/utilities/__tests__/assertValidName-test.ts b/src/utilities/__tests__/assertValidName-test.ts deleted file mode 100644 index 96909d588d..0000000000 --- a/src/utilities/__tests__/assertValidName-test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { expect } from 'chai'; -import { describe, it } from 'mocha'; - -import { assertValidName } from '../assertValidName'; - -describe('assertValidName()', () => { - it('passthrough valid name', () => { - expect(assertValidName('_ValidName123')).to.equal('_ValidName123'); - }); - - it('throws for use of leading double underscores', () => { - expect(() => assertValidName('__bad')).to.throw( - '"__bad" must not begin with "__", which is reserved by GraphQL introspection.', - ); - }); - - it('throws for non-strings', () => { - // @ts-expect-error - expect(() => assertValidName({})).to.throw('Expected name to be a string.'); - }); - - it('throws on empty strings', () => { - expect(() => assertValidName('')).to.throw( - 'Expected name to be a non-empty string.', - ); - }); - - it('throws for names with invalid characters', () => { - expect(() => assertValidName('>--()-->')).to.throw( - 'Names must only contain [_a-zA-Z0-9] but ">--()-->" does not.', - ); - }); - - it('throws for names starting with invalid characters', () => { - expect(() => assertValidName('42MeaningsOfLife')).to.throw( - 'Names must start with [_a-zA-Z] but "42MeaningsOfLife" does not.', - ); - }); -}); diff --git a/src/utilities/assertValidName.ts b/src/utilities/assertValidName.ts index 988a8efafb..45c30ff481 100644 --- a/src/utilities/assertValidName.ts +++ b/src/utilities/assertValidName.ts @@ -1,11 +1,14 @@ import { devAssert } from '../jsutils/devAssert'; import { GraphQLError } from '../error/GraphQLError'; -import { isNameStart, isNameContinue } from '../language/characterClasses'; + +import { assertName } from '../type/assertName'; /** * Upholds the spec rules about naming. + * @deprecated Please use `assertName` instead. Will be removed in v17 */ +// istanbul ignore next (Deprecated code) export function assertValidName(name: string): string { const error = isValidNameError(name); if (error) { @@ -16,7 +19,9 @@ export function assertValidName(name: string): string { /** * Returns an Error if a name is invalid. + * @deprecated Please use `assertName` instead. Will be removed in v17 */ +// istanbul ignore next (Deprecated code) export function isValidNameError(name: string): GraphQLError | undefined { devAssert(typeof name === 'string', 'Expected name to be a string.'); @@ -26,21 +31,9 @@ export function isValidNameError(name: string): GraphQLError | undefined { ); } - if (name.length === 0) { - return new GraphQLError('Expected name to be a non-empty string.'); - } - - for (let i = 1; i < name.length; ++i) { - if (!isNameContinue(name.charCodeAt(i))) { - return new GraphQLError( - `Names must only contain [_a-zA-Z0-9] but "${name}" does not.`, - ); - } - } - - if (!isNameStart(name.charCodeAt(0))) { - return new GraphQLError( - `Names must start with [_a-zA-Z] but "${name}" does not.`, - ); + try { + assertName(name); + } catch (error) { + return error; } }